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

org.spdx.tools.schema.OwlToXsd Maven / Gradle / Ivy

There is a newer version: 2.0.0-Alpha
Show newest version
/**
 * Copyright (c) 2020 Source Auditor Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 * 
 *   Licensed 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 org.spdx.tools.schema;

import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Nullable;
import javax.xml.namespace.QName;

import org.apache.jena.ontology.Individual;
import org.apache.jena.ontology.OntClass;
import org.apache.jena.ontology.OntModel;
import org.apache.jena.ontology.OntProperty;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAnnotated;
import org.apache.ws.commons.schema.XmlSchemaAnnotation;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaComplexContent;
import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaContentType;
import org.apache.ws.commons.schema.XmlSchemaDocumentation;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaEnumerationFacet;
import org.apache.ws.commons.schema.XmlSchemaForm;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSerializer.XmlSchemaSerializerException;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.apache.ws.commons.schema.XmlSchemaType;
import org.apache.ws.commons.schema.utils.NamespaceMap;
import org.spdx.library.SpdxConstants;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * Converts from OWL RDF/XML to XML Schema (XSD)
 * 
 * 
 * @author Gary O'Neall
 *
 */
public class OwlToXsd extends AbstractOwlRdfConverter {
	
	XmlSchemaCollection schemas = new XmlSchemaCollection();

	public OwlToXsd(OntModel model) {
		super(model);
	}
	
	public XmlSchema convertToXsd() throws XmlSchemaSerializerException, SchemaException {
		if (!ontology.isURIResource()) {
			throw new SchemaException("Ontology is not a URI resource");
		}
		String nameSpace = ontology.getURI();
		XmlSchema schema = new XmlSchema(nameSpace, schemas);
		schema.setSchemaNamespacePrefix(nameSpace);
		schema.setInputEncoding(StandardCharsets.UTF_8.name());
		schema.setElementFormDefault(XmlSchemaForm.QUALIFIED);
		NamespaceMap namespaces = new NamespaceMap();
		namespaces.add("xs", "http://www.w3.org/2001/XMLSchema");
		schema.setNamespaceContext(namespaces);
		addDocumentation(schema, schema, ontology.getComment(null));

		ExtendedIterator classIter = model.listClasses().filterKeep(ontClass -> {
			return ontClass.isURIResource();
		});
		while (classIter.hasNext()) {
			OntClass type = classIter.next();
			addTypeToSchema(schema, type);
		}
		
		// Add the top level element
		XmlSchemaElement documentElement = new XmlSchemaElement(schema, true);
		documentElement.setName("Document");
		documentElement.setSchemaTypeName(new QName(SpdxConstants.SPDX_NAMESPACE.substring(0, SpdxConstants.SPDX_NAMESPACE.length()-1), "SpdxDocument"));
		addDocumentation(schema, documentElement, "Top level element for the SPDX document");
		return schema;
	}

	private void addTypeToSchema(XmlSchema schema, OntClass type) throws XmlSchemaSerializerException, SchemaException {
		ExtendedIterator individualIter = model.listIndividuals(type);
		if (individualIter.hasNext()) {
			// Enum type
			addEnumTypeToSchema(schema, type, individualIter.toList());
		} else {
			addComplexTypeToSchema(schema, type);
		}
	}
	
	private XmlSchemaType addComplexTypeToSchema(XmlSchema schema, OntClass type) throws XmlSchemaSerializerException, SchemaException {
		XmlSchemaComplexType xmlType = new XmlSchemaComplexType(schema, true);
		xmlType.setName(type.getLocalName());
		addDocumentation(schema, xmlType, type.getComment(null));
		XmlSchemaComplexContentExtension schemaExtension = null;
		ExtendedIterator superClassIter = type.listSuperClasses(true).filterKeep(sc -> {
			return sc.isURIResource() && 
					!"http://www.w3.org/2000/01/rdf-schema#Container".equals(sc.getURI());
		});
		
		if (superClassIter.hasNext()) {
			OntClass superClass = superClassIter.next();
			schemaExtension = new XmlSchemaComplexContentExtension();
			schemaExtension.setBaseTypeName(new QName(SpdxConstants.SPDX_NAMESPACE.substring(0, SpdxConstants.SPDX_NAMESPACE.length()-1), superClass.getLocalName()));
		}
		if (superClassIter.hasNext()) {
			throw new SchemaException("Ambiguous superclasses for "+type.getLocalName());
		}
		Collection ontProperties = propertiesFromClassRestrictions(type, true);
		xmlType.setContentType(XmlSchemaContentType.ELEMENT_ONLY);
		XmlSchemaSequence sequence = new XmlSchemaSequence();
		for (OntProperty property:ontProperties) {
			if (SKIPPED_PROPERTIES.contains(property.getURI())) {
				continue;
			}
			PropertyRestrictions restrictions = getPropertyRestrictions(type, property);
			XmlSchemaElement propertyMember = new XmlSchemaElement(schema, false);
			propertyMember.setName(property.getLocalName());
			Optional rdfType = getPropertyType(property);
			if (rdfType.isPresent()) {
				String typeNamespace;
				if (rdfType.get().getNameSpace().equals(SpdxConstants.RDF_POINTER_NAMESPACE) || 
						rdfType.get().getNameSpace().equals(SpdxConstants.DOAP_NAMESPACE)) {
					typeNamespace = SpdxConstants.SPDX_NAMESPACE.substring(0, SpdxConstants.SPDX_NAMESPACE.length()-1);
				} else {
					typeNamespace = rdfType.get().getNameSpace().substring(0, rdfType.get().getNameSpace().length()-1);
				}
				propertyMember.setSchemaTypeName(
						new QName(typeNamespace, rdfType.get().getLocalName()));
			}
			addDocumentation(schema, propertyMember, property.getComment(null));
			if (restrictions.isOptional()) {
				propertyMember.setMinOccurs(0);
			} else {
				propertyMember.setMinOccurs(1);
			}
			if (restrictions.isListProperty()) {
				if (restrictions.getMinCardinality() > -1) {
					propertyMember.setMinOccurs(restrictions.getMinCardinality());
				}
				if (restrictions.getMaxCardinality() > -1) {
					propertyMember.setMaxOccurs(restrictions.getMaxCardinality());
				} else {
					propertyMember.setMaxOccurs(Long.MAX_VALUE);
				}
			} else {
				propertyMember.setMaxOccurs(1);
			}
			//TODO: Change the propertyMember to ref=
			sequence.getItems().add(propertyMember);
		}
		if (type.getLocalName().equals(SpdxConstants.CLASS_SPDX_ELEMENT)) {
			// Manually add in SPDXID
			XmlSchemaElement propertyMember = new XmlSchemaElement(schema, false);
			propertyMember.setName("SPDXID");
			propertyMember.setSchemaTypeName(
					new QName("http://www.w3.org/2001/XMLSchema", "string"));
			sequence.getItems().add(propertyMember);
		}
		XmlSchemaComplexContent contentModel = new XmlSchemaComplexContent();
		if (Objects.nonNull(schemaExtension)) {
			if (sequence.getItems().size() > 0) {
				schemaExtension.setParticle(sequence);
			}
			contentModel.setContent(schemaExtension);
			xmlType.setContentModel(contentModel);
		} else if (sequence.getItems().size() > 0) {
			xmlType.setParticle(sequence);
		} else {
			xmlType.setAbstract(true);
		}
		return xmlType;
	}

	private void addEnumTypeToSchema(XmlSchema schema, OntClass type, List individuals) throws XmlSchemaSerializerException {
		XmlSchemaSimpleType xmlType = new XmlSchemaSimpleType(schema, true);
		xmlType.setName(type.getLocalName());
		addDocumentation(schema, xmlType, type.getComment(null));
		XmlSchemaSimpleTypeRestriction xmlContent = new XmlSchemaSimpleTypeRestriction();
		xmlContent.setBaseTypeName(new QName("http://www.w3.org/2001/XMLSchema","string"));
		for (Individual individual:individuals) {
			XmlSchemaEnumerationFacet xmlEnum = new XmlSchemaEnumerationFacet();
			xmlEnum.setValue(individual.getLocalName());
			addDocumentation(schema, xmlEnum, individual.getComment(null));
			xmlContent.getFacets().add(xmlEnum);
		}
		xmlType.setContent(xmlContent);
	}

	/**
	 * Add documentation annotation to an XML schema node
	 * @param schema XML Schema
	 * @param xmlSchemaObject Schema object to add documentation
	 * @param documentation Documentation to add
	 * @throws XmlSchemaSerializerException 
	 */
	private void addDocumentation(XmlSchema schema, XmlSchemaAnnotated xmlSchemaObject, @Nullable String documentation) throws XmlSchemaSerializerException {
		Objects.requireNonNull(xmlSchemaObject);
		if (Objects.nonNull(documentation)) {
			XmlSchemaAnnotation annotation = new XmlSchemaAnnotation();
			XmlSchemaDocumentation schemaDoc = new XmlSchemaDocumentation();
			xmlSchemaObject.setAnnotation(annotation);
			final Text commentNode = schema.getSchemaDocument().createTextNode(documentation);
			NodeList markup = new NodeList() {

				@Override
				public Node item(int index) {
					if (index == 0) {
						return commentNode;
					} else {
						return null;
					}
				}

				@Override
				public int getLength() {
					return 1;
				}
				
			};
			schemaDoc.setMarkup(markup);
			annotation.getItems().add(schemaDoc);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy