org.plasma.sdo.helper.PlasmaXSDHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.plasma.sdo.helper.PlasmaXSDHelper.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.sdo.helper;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.transform.Source;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.modeldriven.fuml.Fuml;
import org.modeldriven.fuml.io.ResourceArtifact;
import org.plasma.common.bind.BindingValidationEventHandler;
import org.plasma.common.bind.DefaultValidationEventHandler;
import org.plasma.common.exception.PlasmaRuntimeException;
import org.plasma.config.PlasmaConfig;
import org.plasma.provisioning.Model;
import org.plasma.provisioning.ModelAppInfo;
import org.plasma.provisioning.ProvisioningModelAssembler;
import org.plasma.provisioning.ProvisioningModelDataBinding;
import org.plasma.provisioning.SchemaProvisioningModelAssembler;
import org.plasma.provisioning.Class;
import org.plasma.provisioning.adapter.ModelAdapter;
import org.plasma.query.Query;
import org.plasma.sdo.PlasmaProperty;
import org.plasma.sdo.PlasmaType;
import org.plasma.sdo.repository.PlasmaRepository;
import org.plasma.xml.schema.OpenAttrs;
import org.plasma.xml.schema.Appinfo;
import org.plasma.xml.schema.Annotation;
import org.plasma.xml.schema.Schema;
import org.plasma.xml.schema.SchemaModelAssembler;
import org.plasma.xml.schema.SchemaDataBinding;
import org.plasma.xml.uml.MDModelAssembler;
import org.plasma.xml.uml.UMLModelAssembler;
import org.xml.sax.SAXException;

import commonj.sdo.Property;
import commonj.sdo.Type;
import commonj.sdo.helper.XSDHelper;

public class PlasmaXSDHelper implements XSDHelper {
    private static Log log = LogFactory.getLog(PlasmaXSDHelper.class);

    static public PlasmaXSDHelper INSTANCE = initializeInstance();

    private PlasmaXSDHelper() {
    }

    private static synchronized PlasmaXSDHelper initializeInstance() {
        if (INSTANCE == null)
            INSTANCE = new PlasmaXSDHelper();
        return INSTANCE;
    }

    /**
     * Define the XML Schema as Types.
     * The Types are available through TypeHelper and DataGraph getType() methods.
     * Same as define(new StringReader(xsd), null)
     * @param xsd the XML Schema.
     * @return the defined Types.
     * @throws IllegalArgumentException if the Types could not be defined.
     */
    public List<Type> define(String xsd) {
        if (log.isDebugEnabled())
            log.debug("unmarshaling schema");
        Schema schema = unmarshalSchema(xsd);

        return define(schema, null);
    }

    /**
     * Define XML Schema as Types.
     * The Types are available through TypeHelper and DataGraph getType() methods.
     * @param xsdReader reader to an XML Schema.
     * @param schemaLocation the URI of the location of the schema, used 
     *   for processing relative imports and includes.  May be null if not used.
     * @return the defined Types.
     * @throws IllegalArgumentException if the Types could not be defined.
     */
    public List<Type> define(Reader xsdReader, String schemaLocation) {
        if (log.isDebugEnabled())
            log.debug("unmarshaling schema");
        Schema schema = unmarshalSchema(xsdReader);

        return define(schema, schemaLocation);
    }

    /**
     * Define XML Schema as Types.
     * The Types are available through TypeHelper and DataGraph getType() methods.
     * @param xsdInputStream input stream to an XML Schema.
     * @param schemaLocation the URI of the location of the schema, used 
     *   for processing relative imports and includes.  May be null if not used.
     * @return the defined Types.
     * @throws IllegalArgumentException if the Types could not be defined.
     */
    public List<Type> define(InputStream xsdInputStream, String schemaLocation) {

        if (log.isDebugEnabled())
            log.debug("unmarshaling schema");
        Schema schema = unmarshalSchema(xsdInputStream);

        return define(schema, schemaLocation);
    }

    private List<Type> define(Schema schema, String schemaLocation) {

        if (log.isDebugEnabled())
            log.debug("provisioning UML/XMI model");
        SchemaProvisioningModelAssembler stagingAssembler = new SchemaProvisioningModelAssembler(schema,
                schema.getTargetNamespace(), "tns");
        Model stagingModel = stagingAssembler.getModel();
        if (log.isDebugEnabled())
            writeSchemaStagingModel(stagingModel, schemaLocation,
                    this.getClass().getSimpleName() + "-" + schema.getId() + "-model.xml");

        ModelAdapter helper = new ModelAdapter(stagingModel);

        UMLModelAssembler assembler = new MDModelAssembler(stagingModel, schema.getTargetNamespace(), "tns");
        String xmi = assembler.getContent();
        if (log.isDebugEnabled()) {
            File xmiDebugFile = null;
            try {
                if (schemaLocation != null)
                    xmiDebugFile = new File(schemaLocation,
                            this.getClass().getSimpleName() + "-" + schema.getId() + "-model.mdxml");
                else
                    xmiDebugFile = File.createTempFile(schema.getId(), "mdxml");
            } catch (IOException e1) {
            }
            log.debug("Writing UML/XMI to: " + xmiDebugFile.getAbsolutePath());
            try {
                FileOutputStream os = new FileOutputStream(xmiDebugFile);
                assembler.getContent(os);
            } catch (FileNotFoundException e) {
            }
        }

        ByteArrayInputStream stream = new ByteArrayInputStream(xmi.getBytes());

        if (log.isDebugEnabled())
            log.debug("loading UML/XMI model");
        Fuml.load(new ResourceArtifact(schema.getTargetNamespace(), schema.getTargetNamespace(), stream));

        // ok we dynamically create a new SDO namespace
        // but now what about linkages to DAS specific
        // namespace configs. Can/should these go away
        // somehow ??
        ModelAppInfo modelAppInfo = getModelAppInfo(schema);
        if (modelAppInfo != null) {
            if (log.isDebugEnabled())
                log.debug("supplier: " + modelAppInfo.getDerivation().getPackageSupplier().getUri());
            PlasmaConfig.getInstance().addDynamicSDONamespace(schema.getTargetNamespace(),
                    modelAppInfo.getDerivation().getPackageSupplier().getUri());
        } else
            PlasmaConfig.getInstance().addDynamicSDONamespace(schema.getTargetNamespace(), null);

        List<Class> entities = stagingAssembler.getModel().getClazzs();
        List<Type> result = new ArrayList<Type>(entities.size());
        for (Class cls : entities) {
            result.add(PlasmaTypeHelper.INSTANCE.getType(cls.getUri(), cls.getName()));
        }

        return result;

    }

    private ModelAppInfo getModelAppInfo(Schema schema) {
        ModelAppInfo modelAppInfo = null;
        for (OpenAttrs attr : schema.getIncludesAndImportsAndRedefines()) {
            if (attr instanceof Annotation) {
                Annotation annot = (Annotation) attr;
                for (Object obj : annot.getAppinfosAndDocumentations()) {
                    if (obj instanceof Appinfo) {
                        for (Object obj2 : ((Appinfo) obj).getContent()) {
                            if (obj2 instanceof ModelAppInfo) {
                                modelAppInfo = (ModelAppInfo) obj2; // whew!
                                break;
                            }
                        }
                    }
                }
            }
        }
        return modelAppInfo;
    }

    /**
     * Generate an XML Schema Declaration (XSD) from Types.
     * Same as generate(types, null);
     * @param types a List containing the Types
     * @return a String containing the generated XSD. 
     * @throws IllegalArgumentException if the XSD could not be generated.
     */
    public String generate(List<Type> types) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Generate an XML Schema Declaration (XSD) from Types.
     * Round trip from SDO to XSD to SDO is supported.
     * Round trip from XSD to SDO to XSD is not supported.
     *  Use the original schema if one exists instead of generating a new one, as
     *  the generated XSD validates a different set of documents than the original XSD.
     * Generating an XSD does not affect the XSDHelper or the Types.
     * The Types must all have the same URI.
     * The result is a String containing the generated XSD. 
     * All Types referenced with the same URI will be generated in the XSD
     *  and the list will be expanded to include all types generated.
     * Any Types referenced with other URIs will cause 
     *  imports to be produced as appropriate.
     * Imports will include a schemaLocation if a Map is provided with an entry
     *  of the form key=import target namespace, value=schemaLocation
     * @param types a List containing the Types
     * @param namespaceToSchemaLocation map of target namespace to schema locations or null
     * @return a String containing the generated XSD. 
     * @throws IllegalArgumentException if the XSD could not be generated.
     */
    public String generate(List<Type> types, Map<String, String> namespaceToSchemaLocation) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Generate an XML Schema Declaration (XSD) with dynamically determined 
     * containment references as embodied within the given, 
     * PlasmaQuery<sup>TM</sup>, query. 
     * Round trip from SDO to XSD to SDO is supported.
     * Round trip from XSD to SDO to XSD is not supported.
     * Generating an XSD does not affect the XSDHelper or the Types.
     * The Types may have different URIs.
     * The result is a String containing the generated XSD. 
     * Imports will include a schemaLocation if a Map is provided with an entry
     *  of the form key=import target namespace, value=schemaLocation
     * @param query the Query used to determine selected types and properties, their 
     * ordering, local names and other information
     * @param targetNamespaceURI the target namespace for the resulting XSD
     * @param namespaceToSchemaLocation map of target namespace to schema locations or null
     * @return a String containing the generated XSD. 
     * @throws IllegalArgumentException if the XSD could not be generated.
     * @see org.plasma.query.PathNode
     * @see org.plasma.query.PathNode#getSelectClause
     * @see org.plasma.query.Select
     */
    public String generate(Query query, String targetNamespaceURI, String targetNamespacePrefix,
            Map<String, String> namespaceToSchemaLocation) {
        if (targetNamespaceURI == null || targetNamespaceURI.trim().length() == 0)
            throw new IllegalArgumentException("expected argument 'targetNamespaceURI'");
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        generate(query, targetNamespaceURI, targetNamespacePrefix, namespaceToSchemaLocation, stream);

        return new String(stream.toByteArray());
    }

    public void generate(Query query, String targetNamespaceURI, String targetNamespacePrefix,
            Map<String, String> namespaceToSchemaLocation, OutputStream xsdOutputStream) {
        if (targetNamespaceURI == null || targetNamespaceURI.trim().length() == 0)
            throw new IllegalArgumentException("expected argument 'targetNamespaceURI'");

        ProvisioningModelAssembler assembler = new ProvisioningModelAssembler(query, targetNamespaceURI,
                targetNamespacePrefix);
        Model model = assembler.getModel();
        if (log.isDebugEnabled())
            writeSchemaStagingModel(model, ".",
                    this.getClass().getSimpleName() + "-" + query.getName() + "-model.xml");
        SchemaModelAssembler builder = new SchemaModelAssembler(model, targetNamespaceURI, targetNamespacePrefix);
        Schema schema = builder.getSchema();

        this.marshalSchema(schema, xsdOutputStream);
    }

    @Override
    public String getAppinfo(Type type, String source) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getAppinfo(Property property, String source) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Property getGlobalProperty(String uri, String propertyName, boolean isElement) {
        // TODO Auto-generated method stub
        return null;
    }

    public String getLocalName(Type type) {
        return ((PlasmaType) type).getLocalName();
    }

    public String getLocalName(Property property) {
        return ((PlasmaProperty) property).getLocalName();
    }

    public String getNamespaceURI(Property property) {
        return property.getContainingType().getURI();
    }

    public boolean isAttribute(Property property) {
        return ((PlasmaProperty) property).isXMLAttribute();
    }

    public boolean isElement(Property property) {
        return !((PlasmaProperty) property).isXMLAttribute();
    }

    @Override
    public boolean isMixed(Type type) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isXSD(Type type) {
        // TODO Auto-generated method stub
        return false;
    }

    private void marshalSchema(Schema schema, OutputStream stream) {
        try {
            SchemaDataBinding binding = new SchemaDataBinding(new DefaultValidationEventHandler());

            binding.marshal(schema, stream);
        } catch (JAXBException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        } catch (SAXException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        }
    }

    private Schema unmarshalSchema(InputStream stream) {
        try {
            SchemaDataBinding binding = new SchemaDataBinding(new DefaultValidationEventHandler());
            return (Schema) binding.unmarshal(stream);

        } catch (JAXBException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        } catch (SAXException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        }
    }

    private Schema unmarshalSchema(String xml) {
        try {
            SchemaDataBinding binding = new SchemaDataBinding(new DefaultValidationEventHandler());
            return (Schema) binding.unmarshal(xml);

        } catch (JAXBException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        } catch (SAXException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        }
    }

    private Schema unmarshalSchema(Reader reader) {
        try {
            SchemaDataBinding binding = new SchemaDataBinding(new DefaultValidationEventHandler());
            return (Schema) binding.unmarshal(reader);

        } catch (JAXBException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        } catch (SAXException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        }
    }

    private Schema unmarshalSchema(Source source) {
        try {
            SchemaDataBinding binding = new SchemaDataBinding(new DefaultValidationEventHandler());
            return (Schema) binding.unmarshal(source);

        } catch (JAXBException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        } catch (SAXException e) {
            log.error(e.getMessage(), e);
            throw new PlasmaRuntimeException(e);
        }
    }

    private void writeSchemaStagingModel(Model stagingModel, String location, String fileName) {
        try {
            BindingValidationEventHandler debugHandler = new BindingValidationEventHandler() {
                public int getErrorCount() {
                    return 0;
                }

                public boolean handleEvent(ValidationEvent ve) {
                    ValidationEventLocator vel = ve.getLocator();

                    String message = "Line:Col:Offset[" + vel.getLineNumber() + ":" + vel.getColumnNumber() + ":"
                            + String.valueOf(vel.getOffset()) + "] - " + ve.getMessage();

                    switch (ve.getSeverity()) {
                    default:
                        log.debug(message);
                    }
                    return true;
                }
            };
            ProvisioningModelDataBinding binding = new ProvisioningModelDataBinding(debugHandler);
            String xml = binding.marshal(stagingModel);
            binding.validate(xml);

            File provDebugFile = null;
            if (location != null)
                provDebugFile = new File(location, fileName);
            else
                provDebugFile = File.createTempFile(fileName, "");
            FileOutputStream provDebugos = new FileOutputStream(provDebugFile);
            log.debug("Writing provisioning model to: " + provDebugFile.getAbsolutePath());
            binding.marshal(stagingModel, provDebugos);
        } catch (JAXBException e) {
            log.debug(e.getMessage(), e);
        } catch (SAXException e) {
            log.debug(e.getMessage(), e);
        } catch (IOException e) {
            log.debug(e.getMessage(), e);
        }

    }

}