org.plasma.xml.schema.SchemaModelAssembler.java Source code

Java tutorial

Introduction

Here is the source code for org.plasma.xml.schema.SchemaModelAssembler.java

Source

/**
 *         PlasmaSDO License
 * 
 * This is a community release of PlasmaSDO, a dual-license 
 * Service Data Object (SDO) 2.1 implementation. 
 * This particular copy of the software is released under the 
 * version 2 of the GNU General Public License. PlasmaSDO was developed by 
 * TerraMeta Software, Inc.
 * 
 * Copyright (c) 2013, TerraMeta Software, Inc. All rights reserved.
 * 
 * General License information can be found below.
 * 
 * This distribution may include materials developed by third
 * parties. For license and attribution notices for these
 * materials, please refer to the documentation that accompanies
 * this distribution (see the "Licenses for Third-Party Components"
 * appendix) or view the online documentation at 
 * <http://plasma-sdo.org/licenses/>.
 *  
 */
package org.plasma.xml.schema;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.plasma.provisioning.Class;
import org.plasma.provisioning.ClassAppInfo;
import org.plasma.provisioning.DataTypeRef;
import org.plasma.provisioning.Documentation;
import org.plasma.provisioning.Model;
import org.plasma.provisioning.ModelAppInfo;
import org.plasma.provisioning.PropertyAppInfo;
import org.plasma.provisioning.ProvisioningConstants;
import org.plasma.provisioning.VisibilityType;
import org.plasma.provisioning.adapter.ModelAdapter;
import org.plasma.provisioning.adapter.TypeAdapter;
import org.plasma.sdo.DataFlavor;
import org.plasma.sdo.DataType;
import org.plasma.sdo.PlasmaProperty;
import org.plasma.sdo.PlasmaType;
import org.plasma.sdo.ValueConstraint;
import org.plasma.sdo.helper.PlasmaTypeHelper;
import org.plasma.sdo.helper.PlasmaXSDHelper;
import org.plasma.sdo.repository.Comment;
import org.plasma.xml.sdox.BaseDataGraphType;
import org.plasma.xml.sdox.SDOXConstants;

import commonj.sdo.Type;

/**
 * Creates an XML Schema model. 
 * @see org.plasma.query.PathNode
 * @see org.plasma.query.PathNode#getSelectClause
 * @see org.plasma.query.Select
 */
//FIXME: this class needs to work with only provisioning structures
public class SchemaModelAssembler {

    private static Log log = LogFactory.getLog(SchemaModelAssembler.class);

    private String destNamespaceURI;
    private String destNamespacePrefix;
    private Model model;
    private Schema schema;
    private boolean createNonContainmentReferenceTypes = true;
    private ModelAdapter helper;
    private Map<String, SimpleType> topLevelTypes = new HashMap<String, SimpleType>();

    @SuppressWarnings("unused")
    private SchemaModelAssembler() {
    }

    /**
     * Creates and XML Schema model based on the given PlasmaSDO#8482; Provisioning
     * Model. All class properties within the given model are expected to contain 
     * ordering or sequence information, such that XSD Element sequences can be
     * created with the expected ordering. 
     * 
     * @param model the PlasmaSDO#8482; Provisioning Model
     * @param destNamespaceURI the target namespace URI
     * @param destNamespacePrefix the target namespace prefix
     * @see org.plasma.provisioning.Sequence
     */
    public SchemaModelAssembler(Model model, String destNamespaceURI, String destNamespacePrefix) {
        super();
        this.model = model;
        this.destNamespaceURI = destNamespaceURI;
        this.destNamespacePrefix = destNamespacePrefix;
    }

    public Schema getSchema() {
        construct(this.model, this.destNamespaceURI, this.destNamespacePrefix);
        return this.schema;
    }

    public boolean isCreateNonContainmentReferenceTypes() {
        return createNonContainmentReferenceTypes;
    }

    public void setCreateNonContainmentReferenceTypes(boolean createNonContainmentReferenceTypes) {
        this.createNonContainmentReferenceTypes = createNonContainmentReferenceTypes;
    }

    private void construct(Model model, String destNamespaceURI, String destNamespacePrefix) {
        if (destNamespaceURI == null || destNamespaceURI.trim().length() == 0)
            throw new IllegalArgumentException("expected 'destNamespaceURI' argument");
        if (destNamespacePrefix == null || destNamespacePrefix.trim().length() == 0)
            throw new IllegalArgumentException("expected 'destNamespacePrefix' argument");

        if (log.isDebugEnabled())
            log.debug("constructing schema for model: " + model.getName());
        this.helper = new ModelAdapter(model);

        this.schema = buildSchema(model);

        //buildDatagraphEnvelopeType(model);

        ComplexType serializationBaseType = buildSerializationBaseType();

        TypeAdapter[] types = helper.getTypesArray();
        if (log.isDebugEnabled())
            log.debug("processing " + types.length + " types");

        for (TypeAdapter adapter : types) {
            if (!(adapter.getType() instanceof Class)) {
                if (log.isDebugEnabled())
                    log.debug("ignoring type: " + adapter.getType().getName());
                continue;
            }
            Class clss = (Class) adapter.getType();
            PlasmaType sdoType = (PlasmaType) PlasmaTypeHelper.INSTANCE.getType(adapter.getUri(),
                    adapter.getName());
            if (log.isDebugEnabled())
                log.debug("processing class: " + clss.getName());

            ComplexType complexType = null;
            if (clss.getSuperClasses().size() == 0) {
                ComplexType baseType = serializationBaseType;
                complexType = buildComplexType(adapter, baseType);
                if (log.isDebugEnabled())
                    log.debug("created complex type: " + complexType.getName());
                addDatatypeAttributesAndElements(complexType, baseType, adapter, sdoType);
            } else {
                if (clss.getSuperClasses().size() > 1)
                    throw new IllegalStateException(
                            "cannot process multiple base classes for type, " + adapter.getKey());
                complexType = buildComplexType(adapter, clss.getSuperClasses().get(0).getName());
                if (log.isDebugEnabled())
                    log.debug("created complex type: " + complexType.getName());
                addDatatypeAttributesAndElements(complexType, null, adapter, sdoType);
            }

            addReferenceElements(complexType, adapter, sdoType);
        }
    }

    private void addDatatypeAttributesAndElements(ComplexType complexType, ComplexType baseType,
            TypeAdapter adapter, PlasmaType sdoType) {

        // add datatype attributes and elements
        for (org.plasma.provisioning.Property prop : adapter.getDeclaredPropertiesArray()) {
            PlasmaProperty property = (PlasmaProperty) sdoType.getProperty(prop.getName());
            if (!(prop.getType() instanceof DataTypeRef))
                continue; // datatype prop

            // ignore properties provisioned as private 
            //if (prop.getVisibility() != null && 
            //   prop.getVisibility().ordinal() == VisibilityType.PRIVATE.ordinal()) {
            //   continue;
            //}

            //currentType = complexType;
            //if (this.createNonContainmentReferenceTypes) {
            //   if (property.isKey(KeyType.external) && nonContainmentReferenceType != null) {
            //      currentType = nonContainmentReferenceType;
            //   }
            //}
            // FIXME: this should be provisioned as 
            if (property.isXMLAttribute()) {
                Attribute attr = buildDataAttributeModel(adapter, sdoType, prop, property, schema);
                if (complexType.getComplexContent() == null) { // no base type
                    complexType.getAttributesAndAttributeGroups().add(attr);
                } else {
                    complexType.getComplexContent().getExtension().getAttributesAndAttributeGroups().add(attr);
                }
            } else {
                // link the element
                ExplicitGroup group = null;
                if (complexType.getComplexContent() == null) { // no base type
                    group = complexType.getSequence();
                    if (group == null) {
                        group = new ExplicitGroup();
                        complexType.setSequence(group);
                    }
                } else {
                    group = complexType.getComplexContent().getExtension().getSequence();
                    if (group == null) {
                        group = new ExplicitGroup();
                        complexType.getComplexContent().getExtension().setSequence(group);
                    }
                }

                Element element = buildDataElementModel(adapter, sdoType, prop, property, schema);
                group.getElementsAndGroupsAndAlls().add(element);
            }
        }
    }

    private void addReferenceElements(ComplexType complexType, TypeAdapter adapter, PlasmaType sdoType) {
        // add reference elements
        for (org.plasma.provisioning.Property prop : adapter.getDeclaredPropertiesArray()) {
            PlasmaProperty property = (PlasmaProperty) sdoType.getProperty(prop.getName());
            if (prop.getType() instanceof DataTypeRef)
                continue; // datatype prop

            // ignore properties provisioned as private 
            if (prop.getVisibility() != null
                    && prop.getVisibility().ordinal() == VisibilityType.PRIVATE.ordinal()) {
                continue;
            }

            ExplicitGroup group = null;
            if (complexType.getComplexContent() == null) { // no base type
                group = complexType.getSequence();
                if (group == null) {
                    group = new ExplicitGroup();
                    complexType.setSequence(group);
                }
            } else {
                group = complexType.getComplexContent().getExtension().getSequence();
                if (group == null) {
                    group = new ExplicitGroup();
                    complexType.getComplexContent().getExtension().setSequence(group);
                }
            }

            Element element = buildReferenceElementModel(adapter, sdoType, prop, property, schema);

            // link the element
            group.getElementsAndGroupsAndAlls().add(element);
        }
    }

    private Schema buildSchema(Model model) {
        Schema schema = new Schema();
        schema.setId(model.getName());
        schema.setTargetNamespace(this.destNamespaceURI);

        Annotation annotation = new Annotation();
        schema.getIncludesAndImportsAndRedefines().add(annotation);
        if (model.getDocumentations() != null && model.getDocumentations().size() > 0) {
            org.plasma.xml.schema.Documentation docum = new org.plasma.xml.schema.Documentation();
            docum.getContent().add(model.getDocumentations().get(0).getBody().getValue());
            annotation.getAppinfosAndDocumentations().add(docum);
        }

        ModelAppInfo modelAppInfo = new ModelAppInfo();
        modelAppInfo.setId(model.getId());
        modelAppInfo.setName(model.getName());
        modelAppInfo.setUri(model.getUri());
        modelAppInfo.setDerivation(model.getDerivation());

        Appinfo appinfo = new Appinfo();
        appinfo.getContent().add(modelAppInfo);
        annotation.getAppinfosAndDocumentations().add(appinfo);

        QName plasmaNamespace = new QName(ProvisioningConstants.PROVISIONING_NAMESPACE_URI, "plasma");
        schema.getOtherAttributes().put(plasmaNamespace, ProvisioningConstants.PROVISIONING_NAMESPACE_URI);

        QName appNamespace = new QName(schema.getTargetNamespace(), this.destNamespacePrefix,
                this.destNamespacePrefix); // use query name as namespace-prefix
        schema.getOtherAttributes().put(appNamespace, schema.getTargetNamespace());

        QName sdoxNamespace = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.SDOX_NAMESPACE_PREFIX,
                SDOXConstants.SDOX_NAMESPACE_PREFIX);
        schema.getOtherAttributes().put(sdoxNamespace, SDOXConstants.SDOX_NAMESPACE_URI);

        // for xs:documentation xml:lang attrib, etc 
        QName xmlNamespace = new QName(SchemaConstants.XML_NAMESPACE_URI, "xml", "xml");
        schema.getOtherAttributes().put(xmlNamespace, SchemaConstants.XML_NAMESPACE_URI);
        return schema;
    }

    private ComplexType buildDatagraphEnvelopeType(Model model) {
        String name = model.getRootClass().getName() + "Datagraph";

        Element topLevelElement = new Element();
        topLevelElement.setName(name);
        QName topLevelType = new QName(schema.getTargetNamespace(), name, destNamespacePrefix);
        topLevelElement.setType(topLevelType);
        schema.getSimpleTypesAndComplexTypesAndGroups().add(topLevelElement);

        // add a complex type
        ComplexType complexType = new ComplexType();
        complexType.setName(name);
        addAnnotation("SDO Data Graph root type", complexType);
        schema.getSimpleTypesAndComplexTypesAndGroups().add(complexType);

        ComplexContent complexContent = new ComplexContent();
        complexType.setComplexContent(complexContent);
        ExtensionType extension = new ExtensionType();
        complexContent.setExtension(extension);
        QName base = new QName("commonj.sdo", BaseDataGraphType.class.getSimpleName(), "sdo");
        extension.setBase(base);

        Element element = new Element();
        element.setName(model.getRootClass().getName());

        QName elementType = new QName(schema.getTargetNamespace(), model.getRootClass().getName());

        element.setType(elementType);
        element.setMinOccurs(new BigInteger("1"));
        element.setMaxOccurs("1");

        ExplicitGroup group = new ExplicitGroup();
        complexType.getComplexContent().getExtension().setSequence(group);
        group.getElementsAndGroupsAndAlls().add(element);

        return complexType;
    }

    private ComplexType buildSerializationBaseType() {

        // add a top level element referencing the below
        // complex type to keep JAXB happy
        Element topLevelElement = new Element();
        topLevelElement.setName(SchemaUtil.getSerializationBaseTypeName());
        QName topLevelType = new QName(schema.getTargetNamespace(), SchemaUtil.getSerializationBaseTypeName(),
                destNamespacePrefix);
        topLevelElement.setType(topLevelType);
        schema.getSimpleTypesAndComplexTypesAndGroups().add(topLevelElement);

        // add a complex type
        ComplexType complexType = new ComplexType();
        complexType.setName(SchemaUtil.getSerializationBaseTypeName());
        addAnnotation("default XML serialization common base type used to "
                + "generalize any type within a Data Graph for use in an XML "
                + "document as either a containment or non-containment reference", complexType);
        schema.getSimpleTypesAndComplexTypesAndGroups().add(complexType);

        Attribute attr = new Attribute();
        complexType.getAttributesAndAttributeGroups().add(attr);
        attr.setName(SchemaUtil.getSerializationAttributeName());
        attr.setUse("required");
        QName attrType = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI, getSchemaDatatype(DataType.String));
        attr.setType(attrType);

        return complexType;
    }

    private ComplexType buildComplexType(TypeAdapter type) {
        return buildComplexType(type, (String) null);
    }

    private ComplexType buildComplexType(TypeAdapter type, ComplexType baseType) {
        return buildComplexType(type, baseType != null ? baseType.getName() : null);
    }

    private ComplexType buildComplexType(TypeAdapter type, String baseType) {
        // add a top level element referencing the below
        // complex type to keep JAXB happy
        Element topLevelElement = new Element();
        topLevelElement.setName(type.getLocalName());
        QName topLevelType = new QName(schema.getTargetNamespace(), type.getName(), destNamespacePrefix); // use query name as namespace-prefix
        topLevelElement.setType(topLevelType);
        schema.getSimpleTypesAndComplexTypesAndGroups().add(topLevelElement);

        // add a complex type
        ComplexType complexType = new ComplexType();
        complexType.setName(type.getLocalName());
        List<Documentation> docs = type.getDocumentation();
        for (Documentation doc : docs) {
            addAnnotation(doc, complexType);
        }
        schema.getSimpleTypesAndComplexTypesAndGroups().add(complexType);

        // add sdox open attributes
        addSDOXTypeAttributes(type, complexType);

        if (baseType != null) {
            ComplexContent complexContent = new ComplexContent();
            complexType.setComplexContent(complexContent);
            ExtensionType extension = new ExtensionType();
            complexContent.setExtension(extension);
            QName base = new QName(this.destNamespaceURI, baseType, this.destNamespacePrefix);
            extension.setBase(base);
        }

        Class clss = (Class) type.getType();
        ClassAppInfo classAppInfo = new ClassAppInfo();
        classAppInfo.setId(clss.getId());
        classAppInfo.setName(clss.getName());
        classAppInfo.setUri(clss.getUri());
        classAppInfo.setAlias(clss.getAlias());
        classAppInfo.setDerivation(clss.getDerivation());

        Appinfo appinfo = new Appinfo();
        appinfo.getContent().add(classAppInfo);
        Annotation annotation = complexType.getAnnotation();
        if (annotation == null) {
            annotation = new Annotation();
            complexType.setAnnotation(annotation);
        }
        annotation.getAppinfosAndDocumentations().add(appinfo);

        return complexType;
    }

    private ComplexType buildNonContainmentReferenceComplexType(TypeAdapter type) {
        String name = getNonContainmentReferenceComplexTypeName(type);
        // add a top level element referencing the below
        // complex type to keep JAXB happy
        Element topLevelElement = new Element();
        topLevelElement.setName(name);
        QName topLevelType = new QName(schema.getTargetNamespace(), name, destNamespacePrefix); // use query name as namespace-prefix
        topLevelElement.setType(topLevelType);
        schema.getSimpleTypesAndComplexTypesAndGroups().add(topLevelElement);

        // add a complex type
        ComplexType complexType = new ComplexType();
        complexType.setName(name);
        addAnnotation("non-containment reference type for,  " + type.getLocalName(), complexType);
        schema.getSimpleTypesAndComplexTypesAndGroups().add(complexType);

        // add sdox open attributes
        addSDOXTypeAttributes(type, complexType);

        return complexType;
    }

    private String getNonContainmentReferenceComplexTypeName(TypeAdapter type) {

        return SchemaUtil.getNonContainmentReferenceName(type);
    }

    private Element buildReferenceElementModel(TypeAdapter adapter, PlasmaType type,
            org.plasma.provisioning.Property prop, PlasmaProperty property, Schema schema) {
        Element element = new Element();
        element.setName(PlasmaXSDHelper.INSTANCE.getLocalName(property));

        // build the element type based on opposite type
        PlasmaType oppositeType = (PlasmaType) property.getType();
        QName elementType = new QName(schema.getTargetNamespace(),
                PlasmaXSDHelper.INSTANCE.getLocalName(oppositeType));
        // set every reference to the serial base type
        // use XSI type in the XML to specific subtypes
        elementType = new QName(schema.getTargetNamespace(), SchemaUtil.getSerializationBaseTypeName());

        element.setType(elementType);

        if (property.isNullable())
            element.setMinOccurs(new BigInteger("0"));
        else
            element.setMinOccurs(new BigInteger("1"));

        if (!property.isMany())
            element.setMaxOccurs("1");
        else
            element.setMaxOccurs("unbounded");

        if (property.getDescription() != null && property.getDescription().size() > 0) {
            addAnnotation(property.getDescription(), element);
        }

        // add sdox open attributes
        addSDOXPropertyAttributes(property, element);

        commonj.sdo.Property oppositeProp = property.getOpposite();
        if (oppositeProp != null) {
            QName qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_OPPOSITE_PROPERTY,
                    SDOXConstants.SDOX_NAMESPACE_PREFIX);
            element.getOtherAttributes().put(qname, PlasmaXSDHelper.INSTANCE.getLocalName(oppositeProp));
            qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_PROPERTY_TYPE,
                    SDOXConstants.SDOX_NAMESPACE_PREFIX);
        }

        return element;
    }

    private Element buildDataElementModel(TypeAdapter adapter, PlasmaType type,
            org.plasma.provisioning.Property prop, PlasmaProperty property, Schema schema) {
        Element element = new Element();
        element.setName(PlasmaXSDHelper.INSTANCE.getLocalName(property));
        if (property.getRestriction() == null) {
            QName elementType = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI,
                    getSchemaDatatype(DataType.valueOf(PlasmaXSDHelper.INSTANCE.getLocalName(property.getType()))));
            element.setType(elementType);
        } else {
            //LocalSimpleType simpleType = buildLocalSimpleTypeRestriction(property);
            //element.setSimpleType(simpleType);
            SimpleType simpleType = buildTopLevelSimpleTypeEnumerationRestriction(property);
            QName topLevelType = new QName(schema.getTargetNamespace(), simpleType.getName(), destNamespacePrefix);
            element.setType(topLevelType);
        }

        if (property.isNullable())
            element.setMinOccurs(new BigInteger("0"));
        else
            element.setMinOccurs(new BigInteger("1"));

        if (!property.isMany())
            element.setMaxOccurs("1");
        else
            element.setMaxOccurs("unbounded");

        if (property.getDescription() != null && property.getDescription().size() > 0) {
            addAnnotation(property.getDescription(), element);
        }

        // add sdox open attributes
        addSDOXPropertyAttributes(property, element);

        return element;
    }

    private Attribute buildDataAttributeModel(TypeAdapter adapter, PlasmaType type,
            org.plasma.provisioning.Property prop, PlasmaProperty property, Schema schema) {
        Attribute attr = new Attribute();
        attr.setName(PlasmaXSDHelper.INSTANCE.getLocalName(property));

        if (property.getRestriction() == null) {
            // value constraint below will specify XSD data type
            if (property.getValueConstraint() == null) {
                QName attrType = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI, getSchemaDatatype(
                        DataType.valueOf(PlasmaXSDHelper.INSTANCE.getLocalName(property.getType()))));
                attr.setType(attrType);
            }
        } else { // has an enum restriction
            SimpleType simpleType = buildTopLevelSimpleTypeEnumerationRestriction(property);
            QName topLevelType = new QName(schema.getTargetNamespace(), simpleType.getName(), destNamespacePrefix);
            attr.setType(topLevelType);
        }

        if (property.getValueConstraint() != null) {
            if (property.getRestriction() == null) {
                ValueConstraint vc = property.getValueConstraint();
                LocalSimpleType simpleType = buildLocalSimpleTypeValueRestriction(property, vc);
                attr.setSimpleType(simpleType);
            } else
                log.warn("both value constraint and enumeration constraint found for property, " + type.getURI()
                        + "#" + type.getName() + "." + property.getName() + " - ignoring value constraint");
        }

        if (!prop.isNullable())
            attr.setUse("required");
        else
            attr.setUse("optional");

        if (property.getDescription() != null)
            addAnnotation(property.getDescription(), attr);

        // add sdox open attributes
        addSDOXPropertyAttributes(property, attr);

        PropertyAppInfo attribAppInfo = new PropertyAppInfo();
        attribAppInfo.setId(prop.getId());
        attribAppInfo.setName(prop.getName());
        attribAppInfo.setAlias(prop.getAlias());
        attribAppInfo.setDerivation(prop.getDerivation());
        attribAppInfo.setKey(prop.getKey());
        attribAppInfo.setConcurrent(prop.getConcurrent());
        attribAppInfo.setUniqueConstraint(prop.getUniqueConstraint());
        attribAppInfo.setValueConstraint(prop.getValueConstraint());
        attribAppInfo.setEnumerationConstraint(prop.getEnumerationConstraint());
        attribAppInfo.setValueSetConstraint(prop.getValueSetConstraint());
        attribAppInfo.setSort(prop.getSort());
        attribAppInfo.setXmlProperty(prop.getXmlProperty());

        Appinfo appinfo = new Appinfo();
        appinfo.getContent().add(attribAppInfo);
        Annotation annotation = attr.getAnnotation();
        if (annotation == null) {
            annotation = new Annotation();
            attr.setAnnotation(annotation);
        }
        annotation.getAppinfosAndDocumentations().add(appinfo);

        return attr;
    }

    /**
     * Adds SDO XML (sdox) attributes for SDO Type to the given superclass 
     * properties to the given OpenAttrs superclass. 
     * @param type the SDO Type
     * @param annotated
     */
    private void addSDOXTypeAttributes(TypeAdapter type, OpenAttrs openAttributes) {
        QName qname = null;
        if (type.getPhysicalName() != null) {
            qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_ALIAS_NAME,
                    SDOXConstants.SDOX_NAMESPACE_PREFIX);
            openAttributes.getOtherAttributes().put(qname, type.getPhysicalName());
        }

        qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_NAME,
                SDOXConstants.SDOX_NAMESPACE_PREFIX);
        openAttributes.getOtherAttributes().put(qname, type.getName());
    }

    /**
     * Adds SDO XML (sdox) attributes common to both reference and data
     * properties to the given OpenAttrs superclass. 
     * @param property the SDO property
     * @param openAttributes
     */
    private void addSDOXPropertyAttributes(PlasmaProperty property, OpenAttrs openAttributes) {
        QName qname = null;
        // name
        qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_NAME,
                SDOXConstants.SDOX_NAMESPACE_PREFIX);
        openAttributes.getOtherAttributes().put(qname, property.getName());

        if (!property.getType().isDataType()) {
            qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_PROPERTY_TYPE,
                    SDOXConstants.SDOX_NAMESPACE_PREFIX);
            openAttributes.getOtherAttributes().put(qname,
                    destNamespacePrefix + ":" + PlasmaXSDHelper.INSTANCE.getLocalName(property.getType()));
        }

        // alias
        if (property.getAliasNames() != null && property.getAliasNames().size() > 0) {
            qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_ALIAS_NAME,
                    SDOXConstants.SDOX_NAMESPACE_PREFIX);
            openAttributes.getOtherAttributes().put(qname, property.getAliasNames().get(0));
        } else {
            if (!property.isMany()) {
                log.warn("no aliases found for singular property " + property.getContainingType().getURI() + "#"
                        + property.getContainingType().getName() + "." + property.getName());
            }

        }

        qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_DATATYPE,
                SDOXConstants.SDOX_NAMESPACE_PREFIX);
        if (property.getType().isDataType())
            openAttributes.getOtherAttributes().put(qname,
                    PlasmaXSDHelper.INSTANCE.getLocalName(property.getType()));
        else
            openAttributes.getOtherAttributes().put(qname,
                    this.destNamespaceURI + "#" + PlasmaXSDHelper.INSTANCE.getLocalName(property.getType()));

        if (property.isKey()) {

            qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_KEY,
                    SDOXConstants.SDOX_NAMESPACE_PREFIX);
            openAttributes.getOtherAttributes().put(qname, "true");
            qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_KEY_TYPE,
                    SDOXConstants.SDOX_NAMESPACE_PREFIX);
            openAttributes.getOtherAttributes().put(qname, property.getKey().getType().name().toLowerCase());
        }

        // many
        qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_MANY,
                SDOXConstants.SDOX_NAMESPACE_PREFIX);
        openAttributes.getOtherAttributes().put(qname, String.valueOf(property.isMany()));
        // readonly
        qname = new QName(SDOXConstants.SDOX_NAMESPACE_URI, SDOXConstants.LOCAL_NAME_READ_ONLY,
                SDOXConstants.SDOX_NAMESPACE_PREFIX);
        openAttributes.getOtherAttributes().put(qname, String.valueOf(property.isReadOnly()));
    }

    private SimpleType buildTopLevelSimpleTypeEnumerationRestriction(PlasmaProperty property) {
        SimpleType simpleType = topLevelTypes.get(property.getRestriction().getName());
        if (simpleType == null) {
            simpleType = new SimpleType();
            org.plasma.sdo.repository.Enumeration enumeration = property.getRestriction();
            simpleType.setName(enumeration.getName());
            Restriction restriction = buildStringEnumerationRestriction(property);
            simpleType.setRestriction(restriction);

            Annotation annotation = addAnnotation(property.getRestriction().getComments(), simpleType);

            // add the logical enum value as an XSD appinfo
            Appinfo appinfo = new Appinfo();
            appinfo.getContent().add(enumeration.getName());
            annotation.getAppinfosAndDocumentations().add(appinfo);

            topLevelTypes.put(property.getRestriction().getName(), simpleType);
            this.schema.getSimpleTypesAndComplexTypesAndGroups().add(simpleType);
        }
        return simpleType;
    }

    private LocalSimpleType buildLocalSimpleTypeEnumerationRestriction(PlasmaProperty property) {
        LocalSimpleType simpleType = new LocalSimpleType();
        Restriction restriction = buildStringEnumerationRestriction(property);
        simpleType.setRestriction(restriction);
        return simpleType;
    }

    private LocalSimpleType buildLocalSimpleTypeValueRestriction(PlasmaProperty property,
            ValueConstraint valueConstrint) {
        LocalSimpleType simpleType = new LocalSimpleType();

        Restriction restriction = null;
        DataFlavor flavor = property.getDataFlavor();
        if (flavor.ordinal() == DataFlavor.string.ordinal()) {
            restriction = buildStringValueRestriction(property, valueConstrint);
        } else if (flavor.ordinal() == DataFlavor.integral.ordinal()) {
            restriction = buildNumericValueRestriction(property, valueConstrint);
        } else
            throw new IllegalStateException("value constraint found for " + "unsupported data flavor '"
                    + flavor.name() + "' on property " + property.getContainingType().getURI() + "#"
                    + property.getContainingType().getName() + "." + property.getName());

        simpleType.setRestriction(restriction);
        return simpleType;
    }

    private Restriction buildNumericValueRestriction(PlasmaProperty property, ValueConstraint valueConstrint) {
        Restriction restriction = new Restriction();
        QName stringType = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI, "string");
        restriction.setBase(stringType);

        // NOTE: having primitive types here introduces ambiguity, i.e. is null intended??
        if (valueConstrint.getMaxInclusive() != null) {
            JAXBElement<Facet> maxIncl = createNumberFacet("maxInclusive", valueConstrint.getMaxInclusive());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(maxIncl);
        } else if (valueConstrint.getMinInclusive() != null) {
            JAXBElement<Facet> minIncl = createNumberFacet("minInclusive", valueConstrint.getMinInclusive());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(minIncl);
        } else if (valueConstrint.getMinExclusive() != null) {
            JAXBElement<Facet> minExcl = createNumberFacet("minExclusive", valueConstrint.getMinExclusive());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(minExcl);
        } else if (valueConstrint.getMaxExclusive() != null) {
            JAXBElement<Facet> maxExcl = createNumberFacet("maxExclusive", valueConstrint.getMaxExclusive());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(maxExcl);
        }

        if (valueConstrint.getTotalDigits() != null) {
            JAXBElement<Facet> totDig = createNumberFacet("totalDigits", valueConstrint.getTotalDigits());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(totDig);
        }
        if (valueConstrint.getFractionDigits() != null) {
            JAXBElement<Facet> fracDig = createNumberFacet("fractionDigits", valueConstrint.getFractionDigits());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(fracDig);
        }

        return restriction;
    }

    private Restriction buildStringValueRestriction(PlasmaProperty property, ValueConstraint valueConstrint) {
        Restriction restriction = new Restriction();
        QName stringType = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI, "string");
        restriction.setBase(stringType);
        if (valueConstrint.getMaxLength() != null) {
            JAXBElement<Facet> maxLen = createNumberFacet("maxLength", valueConstrint.getMaxLength());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(maxLen);
        } else if (valueConstrint.getMinLength() != null) {
            JAXBElement<Facet> minLen = createNumberFacet("minLength", valueConstrint.getMinLength());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(minLen);
        } else if (valueConstrint.getPattern() != null) {
            Pattern pattern = new Pattern();
            pattern.setValue(valueConstrint.getPattern());
            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(pattern);
        }

        return restriction;
    }

    private JAXBElement<Facet> createNumberFacet(String name, int value) {
        NumFacet facet = new NumFacet();
        facet.setValue(String.valueOf(value));
        QName qname = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI, name);
        return new JAXBElement<Facet>(qname, Facet.class, null, facet);
    }

    private JAXBElement<Facet> createNumberFacet(String name, String value) {
        NumFacet facet = new NumFacet();
        facet.setValue(String.valueOf(value));
        QName qname = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI, name);
        return new JAXBElement<Facet>(qname, Facet.class, null, facet);
    }

    private Restriction buildStringEnumerationRestriction(PlasmaProperty property) {
        Restriction restriction = new Restriction();
        QName stringType = new QName(SchemaConstants.XMLSCHEMA_NAMESPACE_URI, "string");
        restriction.setBase(stringType);

        org.plasma.sdo.repository.Enumeration propertyRestriction = property.getRestriction();

        for (org.plasma.sdo.repository.EnumerationLiteral literal : propertyRestriction.getOwnedLiteral()) {
            Enumeration enumeration = new Enumeration();
            enumeration.setValue(literal.getPhysicalName());

            Annotation annotation = addAnnotation(literal.getComments(), enumeration);

            // add the logical enum value as an XSD appinfo
            Appinfo appinfo = new Appinfo();
            appinfo.getContent().add(literal.getName());
            annotation.getAppinfosAndDocumentations().add(appinfo);

            restriction.getMinExclusivesAndMinInclusivesAndMaxExclusives().add(enumeration);
        }

        return restriction;
    }

    private Annotation addAnnotation(Documentation doc, Annotated annotated) {
        Annotation annotation = new Annotation();
        annotated.setAnnotation(annotation);
        org.plasma.xml.schema.Documentation docum = new org.plasma.xml.schema.Documentation();
        // fails with below even though XML namespace declared
        // "org.xml.sax.SAXParseException: src-resolve: Cannot resolve the name 'xml:lang' to a(n) 'attribute declaration component'"
        //docum.setLang("en");
        docum.getContent().add("" + doc.getBody().getValue());
        annotation.getAppinfosAndDocumentations().add(docum);

        // add appinfo
        //Appinfo appinfo = new Appinfo();
        //appinfo.getContent().add("");
        //annotation.getAppinfosAndDocumentations().add(appinfo);

        return annotation;
    }

    private Annotation addAnnotation(List<Comment> description, Annotated annotated) {
        Annotation annotation = new Annotation();
        annotated.setAnnotation(annotation);
        org.plasma.xml.schema.Documentation docum = new org.plasma.xml.schema.Documentation();
        // fails with below even though XML namespace declared
        // "org.xml.sax.SAXParseException: src-resolve: Cannot resolve the name 'xml:lang' to a(n) 'attribute declaration component'"
        //docum.setLang("en");
        for (Comment comment : description)
            docum.getContent().add("" + comment.getBody());
        annotation.getAppinfosAndDocumentations().add(docum);

        // add appinfo
        //Appinfo appinfo = new Appinfo();
        //appinfo.getContent().add("");
        //annotation.getAppinfosAndDocumentations().add(appinfo);

        return annotation;
    }

    private Annotation addAnnotation(String content, Annotated annotated) {
        Annotation annotation = new Annotation();
        annotated.setAnnotation(annotation);
        org.plasma.xml.schema.Documentation docum = new org.plasma.xml.schema.Documentation();
        // fails with below even though XML namespace declared
        // "org.xml.sax.SAXParseException: src-resolve: Cannot resolve the name 'xml:lang' to a(n) 'attribute declaration component'"
        //docum.setLang("en");
        docum.getContent().add(content);
        annotation.getAppinfosAndDocumentations().add(docum);
        return annotation;
    }

    private String getSchemaDatatype(DataType datatype) {
        switch (datatype) {
        case Boolean:
            return "boolean";
        case Byte:
            return "byte";
        case Bytes:
            return "hexBinary";
        case Character:
            return "string";
        case DateTime:
            return "dateTime";
        case Date:
            return "date";
        case Day:
            return "gDay";
        case Decimal:
            return "decimal";
        case Duration:
            return "duration";
        case Float:
            return "float";
        case Double:
            return "double";
        case Int:
            return "int";
        case Integer:
            return "integer";
        case Long:
            return "long";
        case Month:
            return "gMonth";
        case MonthDay:
            return "gMonthDay";
        case Short:
            return "short";
        case String:
            return "string";
        case Time:
            return "dateTime";
        case URI:
            return "anyURI";
        case Year:
            return "gYear";
        case YearMonth:
            return "gYearMonth";
        case YearMonthDay:
            return "date";
        case Object:
            return "anySimpleType";
        default:
            throw new IllegalArgumentException("unknown datatype, " + datatype.toString());
        }
    }

    private List<String> sort(List<String> list) {
        String[] values = new String[list.size()];
        list.toArray(values);
        Arrays.sort(values);
        List<String> result = new ArrayList<String>(list.size());
        for (String s : values)
            result.add(s);
        return result;
    }

    /*
    SDO Type      XSD Type
    Boolean         boolean
    Byte         byte
    Bytes         hexBinary
    Character      string
    DataObject      anyType
    Date          dateTime 
    DateTime      dateTime
    Day            gDay
    Decimal         decimal
    Double         double
    Duration      duration
    Float         float
    Int            int
    Integer         integer
    Long         long
    Month         gMonth
    MonthDay      gMonthDay
    Object         anySimpleType
    Short         short
    String         string
    Strings         string
    Time         time
    Year         gYear
    YearMonth      gYearMonth
    YearMonthDay   date
    URI            anyURI
            
     */

    /*
    XSD Simple Type       / SDO Type
    anySimpleType         Object
    anyType               DataObject
    anyURI               URI   (override with sdox:propertyType)
    base64Binary         Bytes
    boolean               Boolean
    byte               Byte
    date               YearMonthDay
    dateTime            DateTime
    decimal               Decimal
    double               Double
    duration            Duration
    ENTITIES            Strings
    ENTITY               String
    float               Float
    gDay               Day
    gMonth               Month
    gMonthDay            MonthDay
    gYear               Year
    gYearMonth            YearMonth
    hexBinary            Bytes
    ID                  String (signifies the field is a sdo:key field)
    IDREF               String   (override with sdox: propertyType)
    IDREFS               Strings  (override with sdox: propertyType) 
    int                  Int
    integer               Integer
    language            String
    long               Long
    Name               String
    NCName               String
    negativeInteger         Integer
    NMTOKEN               String
    NMTOKENS            Strings
    nonNegativeInteger      Integer
    nonPositiveInteger      Integer
    normalizedString      String
    NOTATION            String
    positiveInteger         Integer
    QName               URI 
    short               Short
    string               String
    time               Time
    token               String
    unsignedByte         UnsignedByte
    unsignedInt            UnsignedInt
    unsignedLong         UnsignedLong
    unsignedShort         UnsignedShort
         
     */
}