From 8c4421e9cc1bfc8e382b01b9ad9bade99ab96005 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 20 Jul 2026 04:44:15 +0100 Subject: [PATCH 1/3] Migrate to Jena new ontapi OntModel Signed-off-by: Arthit Suriyawongkul --- pom.xml | 5 + .../spdx/tools/RdfSchemaToJsonContext.java | 8 +- .../org/spdx/tools/RdfSchemaToJsonSchema.java | 8 +- .../java/org/spdx/tools/RdfSchemaToXsd.java | 8 +- .../tools/schema/AbstractOwlRdfConverter.java | 162 +++++++++--------- .../spdx/tools/schema/OwlToJsonContext.java | 11 +- .../spdx/tools/schema/OwlToJsonSchema.java | 32 ++-- .../java/org/spdx/tools/schema/OwlToXsd.java | 39 ++--- .../org/spdx/tools/schema/OwlToXSDTest.java | 8 +- 9 files changed, 133 insertions(+), 148 deletions(-) diff --git a/pom.xml b/pom.xml index b821018b..9e73eb83 100644 --- a/pom.xml +++ b/pom.xml @@ -150,6 +150,11 @@ json-schema-validator 1.5.9 + + org.apache.jena + jena-ontapi + 5.6.0 + org.slf4j slf4j-simple diff --git a/src/main/java/org/spdx/tools/RdfSchemaToJsonContext.java b/src/main/java/org/spdx/tools/RdfSchemaToJsonContext.java index 299099a0..010fc546 100644 --- a/src/main/java/org/spdx/tools/RdfSchemaToJsonContext.java +++ b/src/main/java/org/spdx/tools/RdfSchemaToJsonContext.java @@ -26,9 +26,9 @@ import java.io.InputStream; import java.util.Objects; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; import org.spdx.tools.schema.OwlToJsonContext; import com.fasterxml.jackson.core.JsonGenerator; @@ -68,7 +68,7 @@ public static void main(String[] args) { OwlToJsonContext owlToJsonContext = null; try { is = new FileInputStream(fromFile); - OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + OntModel model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); model.read(is, "RDF/XML"); owlToJsonContext = new OwlToJsonContext(model); } catch (FileNotFoundException e) { diff --git a/src/main/java/org/spdx/tools/RdfSchemaToJsonSchema.java b/src/main/java/org/spdx/tools/RdfSchemaToJsonSchema.java index 595257b4..0bd44587 100644 --- a/src/main/java/org/spdx/tools/RdfSchemaToJsonSchema.java +++ b/src/main/java/org/spdx/tools/RdfSchemaToJsonSchema.java @@ -26,9 +26,9 @@ import java.io.InputStream; import java.util.Objects; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; import org.spdx.tools.schema.OwlToJsonSchema; import com.fasterxml.jackson.core.JsonGenerator; @@ -70,7 +70,7 @@ public static void main(String[] args) { OntModel model = null; try { is = new FileInputStream(fromFile); - model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); model.read(is, "RDF/XML"); } catch (FileNotFoundException e) { System.err.println("File not found for "+fromFile.getName()); diff --git a/src/main/java/org/spdx/tools/RdfSchemaToXsd.java b/src/main/java/org/spdx/tools/RdfSchemaToXsd.java index 54829666..ba523b83 100644 --- a/src/main/java/org/spdx/tools/RdfSchemaToXsd.java +++ b/src/main/java/org/spdx/tools/RdfSchemaToXsd.java @@ -27,9 +27,9 @@ import java.io.OutputStream; import java.util.Objects; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaSerializer.XmlSchemaSerializerException; import org.spdx.tools.schema.OwlToXsd; @@ -68,7 +68,7 @@ public static void main(String[] args) { OntModel model = null; try { is = new FileInputStream(fromFile); - model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); model.read(is, "RDF/XML"); } catch (FileNotFoundException e) { System.err.println("File not found for "+fromFile.getName()); diff --git a/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java b/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java index f945e8e9..34ff4c00 100644 --- a/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java +++ b/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java @@ -30,18 +30,18 @@ import java.util.Optional; import java.util.Set; -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.ontology.OntResource; -import org.apache.jena.ontology.Ontology; -import org.apache.jena.ontology.Restriction; -import org.apache.jena.ontology.UnionClass; +import org.apache.jena.ontapi.model.OntClass; +import org.apache.jena.ontapi.model.OntID; +import org.apache.jena.ontapi.model.OntIndividual; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.ontapi.model.OntObject; +import org.apache.jena.ontapi.model.OntProperty; import org.apache.jena.rdf.model.NodeIterator; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.rdf.model.StmtIterator; import org.apache.jena.util.iterator.ExtendedIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,25 +104,24 @@ public PropertyRestrictions(OntClass ontClass, OntProperty property) { Objects.requireNonNull(ontClass, "Missing required ontology class"); Objects.requireNonNull(property, "Missing required property"); this.property = property; - List restrictions = getRestrictionsFromSuperclasses(ontClass, property); + List restrictions = getRestrictionsFromSuperclasses(ontClass, property); interpretRestrictions(restrictions); } - + /** * Convert a list of restrictions for this property into the field values * @param restrictions */ - private void interpretRestrictions(List restrictions) { - for (Restriction r:restrictions) { - RDFNode typePropertyValue = r.getPropertyValue(owlClassProperty); + private void interpretRestrictions(List restrictions) { + for (OntClass.Restriction r:restrictions) { + Statement typeStmt = r.getProperty(owlClassProperty); + RDFNode typePropertyValue = typeStmt != null ? typeStmt.getObject() : null; if (Objects.nonNull(typePropertyValue) && typePropertyValue.isURIResource()) { typeUri = typePropertyValue.asResource().getURI(); // check to see if this type is an enumeration type OntClass typeClass = model.getOntClass(typeUri); if (Objects.nonNull(typeClass)) { - ExtendedIterator individualIter = model.listIndividuals(typeClass); - while (individualIter.hasNext()) { - Individual individual = individualIter.next(); + typeClass.individuals().forEach(individual -> { if (individual.isURIResource()) { Enum e = SpdxEnumFactoryCompatV2.uriToEnum.get(individual.getURI()); if (Objects.nonNull(e)) { @@ -132,13 +131,13 @@ private void interpretRestrictions(List restrictions) { logger.warn("Missing enum value for " + individual.getLocalName()); } } - } + }); } } else { // Check for enumeration types as a direct restriction - NodeIterator hasValueIter = r.listPropertyValues(hasValueProperty); + StmtIterator hasValueIter = r.listProperties(hasValueProperty); while (hasValueIter.hasNext()) { - RDFNode hasValue = hasValueIter.next(); + RDFNode hasValue = hasValueIter.next().getObject(); if (hasValue.isURIResource()) { Enum e = SpdxEnumFactoryCompatV2.uriToEnum.get(hasValue.asResource().getURI()); if (Objects.nonNull(e)) { @@ -149,12 +148,24 @@ private void interpretRestrictions(List restrictions) { } } // cardinality - RDFNode qualCardPropValue = r.getPropertyValue(qualCardProperty); - RDFNode cardPropValue = r.getPropertyValue(cardProperty); - RDFNode maxQualCardPropValue = r.getPropertyValue(maxQualCardProperty); - RDFNode maxCardPropValue = r.getPropertyValue(maxCardProperty); - RDFNode minCardPropValue = r.getPropertyValue(minCardProperty); - RDFNode minQualCardPropValue = r.getPropertyValue(minQualCardProperty); + Statement qualCardStmt = r.getProperty(qualCardProperty); + RDFNode qualCardPropValue = qualCardStmt != null ? qualCardStmt.getObject() : null; + + Statement cardStmt = r.getProperty(cardProperty); + RDFNode cardPropValue = cardStmt != null ? cardStmt.getObject() : null; + + Statement maxQualCardStmt = r.getProperty(maxQualCardProperty); + RDFNode maxQualCardPropValue = maxQualCardStmt != null ? maxQualCardStmt.getObject() : null; + + Statement maxCardStmt = r.getProperty(maxCardProperty); + RDFNode maxCardPropValue = maxCardStmt != null ? maxCardStmt.getObject() : null; + + Statement minCardStmt = r.getProperty(minCardProperty); + RDFNode minCardPropValue = minCardStmt != null ? minCardStmt.getObject() : null; + + Statement minQualCardStmt = r.getProperty(minQualCardProperty); + RDFNode minQualCardPropValue = minQualCardStmt != null ? minQualCardStmt.getObject() : null; + if (Objects.nonNull(qualCardPropValue) && qualCardPropValue.isLiteral()) { absoluteCardinality = qualCardPropValue.asLiteral().getInt(); if (absoluteCardinality > 0) { @@ -205,15 +216,13 @@ private void interpretRestrictions(List restrictions) { } if (Objects.isNull(typeUri)) { // get the type from the range of the property - ExtendedIterator rangeIter = property.listRange(); - while (rangeIter.hasNext()) { - OntResource range = rangeIter.next(); + property.ranges().forEach(range -> { if (range.isURIResource()) { if (Objects.isNull(typeUri) || typeUri.equals("http://www.w3.org/2000/01/rdf-schema#Literal")) { typeUri = range.asResource().getURI(); } } - } + }); } if (Objects.isNull(typeUri) && ("comment".equals(property.getLocalName()) || "seeAlso".equals(property.getLocalName()))) { // A bit of a hack, the schema file can not store the type of rdfs:comment, so we must override it to xsd:string @@ -221,38 +230,33 @@ private void interpretRestrictions(List restrictions) { } } - private List getRestrictionsFromSuperclasses(OntClass ontClass, OntProperty property) { - List retval = new ArrayList<>(); - ExtendedIterator superClasses = ontClass.listSuperClasses(); - while (superClasses.hasNext()) { - OntClass superClass = superClasses.next(); - if (superClass.isUnionClass()) { - UnionClass uClass = superClass.asUnionClass(); - ExtendedIterator unionClassiter = uClass.listOperands(); - while (unionClassiter.hasNext()) { - OntClass operand = unionClassiter.next(); - if (operand.isRestriction() && property.equals(operand.asRestriction().getOnProperty())) { - retval.add(operand.asRestriction()); + private List getRestrictionsFromSuperclasses(OntClass ontClass, OntProperty property) { + List retval = new ArrayList<>(); + ontClass.superClasses(false).forEach(superClass -> { + if (superClass instanceof OntClass.UnionOf) { + OntClass.UnionOf uClass = (OntClass.UnionOf) superClass; + uClass.components().forEach(operand -> { + if (operand instanceof OntClass.UnaryRestriction && property.equals(((OntClass.UnaryRestriction) operand).getProperty())) { + retval.add((OntClass.Restriction) operand); } - } - } else if (superClass.isRestriction()) { - if (property.equals(superClass.asRestriction().getOnProperty())) { - retval.add(superClass.asRestriction()); + }); + } else if (superClass instanceof OntClass.UnaryRestriction) { + OntClass.UnaryRestriction r = (OntClass.UnaryRestriction) superClass; + if (property.equals(r.getProperty())) { + retval.add(r); } - } else { - retval.addAll(getRestrictionsFromSuperclasses(superClass, property)); } - } + }); return retval; } public PropertyRestrictions(OntProperty property) { Objects.requireNonNull(property, "Missing required property"); this.property = property; - List propertyRestrictions = new ArrayList<>(); - ExtendedIterator restrictionIter = property.listReferringRestrictions(); - while (restrictionIter.hasNext()) { - propertyRestrictions.add(restrictionIter.next()); + List propertyRestrictions = new ArrayList<>(); + if (property instanceof org.apache.jena.ontapi.model.OntRelationalProperty) { + ((org.apache.jena.ontapi.model.OntRelationalProperty) property).referringRestrictions() + .forEach(propertyRestrictions::add); } interpretRestrictions(propertyRestrictions); } @@ -342,7 +346,7 @@ public boolean isSingleProperty() { Property restResource; Property intersectionOfProperty; - Ontology ontology; + OntID ontology; public AbstractOwlRdfConverter(OntModel model) { Objects.requireNonNull(model, "Model must not be null"); @@ -360,13 +364,8 @@ public AbstractOwlRdfConverter(OntModel model) { intersectionOfProperty = model.createProperty("http://www.w3.org/2002/07/owl#intersectionOf"); firstResource = model.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#first"); restResource = model.createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest"); - ontology = null; - ExtendedIterator ontIter = model.listOntologies(); - if (!ontIter.hasNext()) { - throw new RuntimeException("No ontologies defined in RDF OWL"); - } - ontology = ontIter.next(); - if (ontIter.hasNext()) { + ontology = model.getID(); + if (Objects.isNull(ontology)) { throw new RuntimeException("No ontologies defined in RDF OWL"); } } @@ -417,13 +416,7 @@ public int compare(OntProperty arg0, OntProperty arg1) { private void removeSuperProperties(Collection properties) { List superProperties = new ArrayList<>(); for (OntProperty property:properties) { - if (property.isProperty()) { - OntProperty op = property.asProperty(); - ExtendedIterator superIter = op.listSuperProperties(); - while (superIter.hasNext()) { - superProperties.add(superIter.next()); - } - } + property.superProperties(false).forEach(superProperties::add); } for (OntProperty superProp:superProperties) { if (properties.contains(superProp)) { @@ -446,9 +439,9 @@ protected Collection propertiesFromClassRestrictions(OntClass oClas * @throws SchemaException */ protected Optional getPropertyType(OntProperty property) throws SchemaException { - ExtendedIterator rangeIter = property.listRange(); + java.util.Iterator rangeIter = property.ranges().iterator(); while (rangeIter.hasNext()) { - OntResource range = rangeIter.next(); + Resource range = rangeIter.next(); if (range.isURIResource()) { return Optional.of(range); } else if (range.hasProperty(unionOfProperty)) { @@ -502,30 +495,29 @@ private void collectPropertiesFromRestrictions(OntClass oClass, return; } reviewedClasses.add(oClass); - if (oClass.isUnionClass()) { - UnionClass uClass = oClass.asUnionClass(); - ExtendedIterator unionClassiter = uClass.listOperands(); - while (unionClassiter.hasNext()) { - collectPropertiesFromRestrictions(unionClassiter.next(), properties, + if (oClass instanceof OntClass.UnionOf) { + OntClass.UnionOf uClass = (OntClass.UnionOf) oClass; + uClass.components().forEach(nextClass -> { + collectPropertiesFromRestrictions(nextClass, properties, reviewedClasses, excludeSuperClassProperties); - } - } else if (oClass.isRestriction()) { - Restriction r = oClass.asRestriction(); - OntProperty property = r.getOnProperty(); + }); + } else if (oClass instanceof OntClass.UnaryRestriction) { + OntClass.UnaryRestriction r = (OntClass.UnaryRestriction) oClass; + OntProperty property = r.getProperty(); if (Objects.nonNull(property)) { properties.add(property); } } else { - ExtendedIterator subClassIter = oClass.listSuperClasses(excludeSuperClassProperties); + java.util.stream.Stream superClassStream = oClass.superClasses(excludeSuperClassProperties); if (excludeSuperClassProperties) { - subClassIter = subClassIter.filterDrop(sc -> { - return sc.isURIResource() && - !"http://www.w3.org/2000/01/rdf-schema#Container".equals(sc.getURI()); + superClassStream = superClassStream.filter(sc -> { + return !sc.isURIResource() || + "http://www.w3.org/2000/01/rdf-schema#Container".equals(sc.getURI()); }); } - while (subClassIter.hasNext()) { - collectPropertiesFromRestrictions(subClassIter.next(), properties, reviewedClasses, excludeSuperClassProperties); - } + superClassStream.forEach(sc -> { + collectPropertiesFromRestrictions(sc, properties, reviewedClasses, excludeSuperClassProperties); + }); } } } diff --git a/src/main/java/org/spdx/tools/schema/OwlToJsonContext.java b/src/main/java/org/spdx/tools/schema/OwlToJsonContext.java index b205af21..99fb017a 100644 --- a/src/main/java/org/spdx/tools/schema/OwlToJsonContext.java +++ b/src/main/java/org/spdx/tools/schema/OwlToJsonContext.java @@ -25,9 +25,8 @@ import java.util.Objects; import java.util.TreeMap; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntProperty; -import org.apache.jena.util.iterator.ExtendedIterator; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.ontapi.model.OntProperty; import org.spdx.jacksonstore.MultiFormatStore; import org.spdx.library.model.v2.SpdxConstantsCompatV2; @@ -79,16 +78,14 @@ public ObjectNode convertToContext() { contexts.put(SpdxConstantsCompatV2.SPDX_IDENTIFIER, "@id"); contexts.put(SpdxConstantsCompatV2.EXTERNAL_DOCUMENT_REF_IDENTIFIER, "@id"); TreeMap sortedOntProperties = new TreeMap<>(); - ExtendedIterator iter = model.listAllOntProperties(); - while (iter.hasNext()) { - OntProperty property = iter.next(); + model.properties().forEach(property -> { if (property.isURIResource()) { String propNamespace = uriToNamespace(property.getURI()); String propName = uriToPropName(property.getURI()); String id = propNamespace + propName; sortedOntProperties.put(id, property); } - } + }); for (Entry ontPropEntry:sortedOntProperties.entrySet()) { String propNamespace = uriToNamespace(ontPropEntry.getValue().getURI()); String propName = uriToPropName(ontPropEntry.getValue().getURI()); diff --git a/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java b/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java index beb7b848..110703f8 100644 --- a/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java +++ b/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java @@ -26,12 +26,11 @@ import javax.annotation.Nullable; -import org.apache.jena.ontology.OntClass; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntProperty; -import org.apache.jena.ontology.Ontology; +import org.apache.jena.ontapi.model.OntClass; +import org.apache.jena.ontapi.model.OntID; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.ontapi.model.OntProperty; import org.apache.jena.rdf.model.Statement; -import org.apache.jena.util.iterator.ExtendedIterator; import org.spdx.jacksonstore.MultiFormatStore; import org.spdx.jacksonstore.SpdxJsonLDContext; import org.spdx.library.model.v2.ReferenceType; @@ -90,20 +89,17 @@ public OwlToJsonSchema(OntModel model) { public ObjectNode convertToJsonSchema() { ObjectNode root = jsonMapper.createObjectNode(); root.put("$schema", SCHEMA_VERSION_URI); - ExtendedIterator ontologyIter = model.listOntologies(); + OntID ont = model.getID(); String version = null; - if (ontologyIter.hasNext()) { - Ontology ont = ontologyIter.next(); - if (ont.isURIResource()) { - version = ont.getVersionInfo(); - String ontologyUri = version == null ? ont.getURI() : ont.getURI() + "/" + version; - if (Objects.nonNull(ontologyUri)) { - root.put("$id", ontologyUri); - } - String title = ont.getLabel(null); - if (Objects.nonNull(title)) { - root.put("title", title); - } + if (ont != null && ont.isURIResource()) { + version = ont.getVersionInfo(); + String ontologyUri = version == null ? ont.getURI() : ont.getURI() + "/" + version; + if (Objects.nonNull(ontologyUri)) { + root.put("$id", ontologyUri); + } + String title = ont.getLabel(null); + if (Objects.nonNull(title)) { + root.put("title", title); } } root.put(JSON_RESTRICTION_TYPE,JSON_TYPE_OBJECT); diff --git a/src/main/java/org/spdx/tools/schema/OwlToXsd.java b/src/main/java/org/spdx/tools/schema/OwlToXsd.java index 60c0a15d..7513bf91 100644 --- a/src/main/java/org/spdx/tools/schema/OwlToXsd.java +++ b/src/main/java/org/spdx/tools/schema/OwlToXsd.java @@ -27,12 +27,11 @@ 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.ontapi.model.OntClass; +import org.apache.jena.ontapi.model.OntIndividual; +import org.apache.jena.ontapi.model.OntModel; +import org.apache.jena.ontapi.model.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; @@ -84,11 +83,8 @@ public XmlSchema convertToXsd() throws XmlSchemaSerializerException, SchemaExcep 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(); + java.util.List classes = model.classes().collect(java.util.stream.Collectors.toList()); + for (OntClass.Named type : classes) { addTypeToSchema(schema, type); } @@ -101,10 +97,10 @@ public XmlSchema convertToXsd() throws XmlSchemaSerializerException, SchemaExcep } private void addTypeToSchema(XmlSchema schema, OntClass type) throws XmlSchemaSerializerException, SchemaException { - ExtendedIterator individualIter = model.listIndividuals(type); - if (individualIter.hasNext()) { + java.util.List individuals = type.individuals().collect(java.util.stream.Collectors.toList()); + if (!individuals.isEmpty()) { // Enum type - addEnumTypeToSchema(schema, type, individualIter.toList()); + addEnumTypeToSchema(schema, type, individuals); } else { addComplexTypeToSchema(schema, type); } @@ -115,17 +111,16 @@ private XmlSchemaType addComplexTypeToSchema(XmlSchema schema, OntClass type) th 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()); - }); + java.util.List superClasses = type.superClasses(true) + .filter(sc -> sc.isURIResource() && !"http://www.w3.org/2000/01/rdf-schema#Container".equals(sc.getURI())) + .collect(java.util.stream.Collectors.toList()); - if (superClassIter.hasNext()) { - OntClass superClass = superClassIter.next(); + if (!superClasses.isEmpty()) { + OntClass superClass = superClasses.get(0); schemaExtension = new XmlSchemaComplexContentExtension(); schemaExtension.setBaseTypeName(new QName(SpdxConstantsCompatV2.SPDX_NAMESPACE.substring(0, SpdxConstantsCompatV2.SPDX_NAMESPACE.length()-1), superClass.getLocalName())); } - if (superClassIter.hasNext()) { + if (superClasses.size() > 1) { throw new SchemaException("Ambiguous superclasses for "+type.getLocalName()); } Collection ontProperties = propertiesFromClassRestrictions(type, true); @@ -194,13 +189,13 @@ private XmlSchemaType addComplexTypeToSchema(XmlSchema schema, OntClass type) th return xmlType; } - private void addEnumTypeToSchema(XmlSchema schema, OntClass type, List individuals) throws XmlSchemaSerializerException { + 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) { + for (OntIndividual individual:individuals) { XmlSchemaEnumerationFacet xmlEnum = new XmlSchemaEnumerationFacet(); xmlEnum.setValue(individual.getLocalName()); addDocumentation(schema, xmlEnum, individual.getComment(null)); diff --git a/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java b/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java index d32d63f8..4d71409d 100644 --- a/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java +++ b/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java @@ -10,9 +10,9 @@ import java.io.IOException; import java.io.InputStream; -import org.apache.jena.ontology.OntModel; -import org.apache.jena.ontology.OntModelSpec; -import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaSerializer.XmlSchemaSerializerException; @@ -33,7 +33,7 @@ protected void tearDown() throws Exception { public void testConvertToXsd() throws IOException, XmlSchemaSerializerException, SchemaException { OwlToXsd otx = null; try (InputStream is = new FileInputStream(new File(OWL_FILE_PATH))) { - OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + OntModel model = OntModelFactory.createModel(OntSpecification.OWL2_DL_MEM); model.read(is, "RDF/XML"); otx = new OwlToXsd(model); } From 3725afd1145701f7e81d287773ea80e1a0fe63a4 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 20 Jul 2026 04:46:47 +0100 Subject: [PATCH 2/3] Remove unused imports Signed-off-by: Arthit Suriyawongkul --- .../java/org/spdx/tools/schema/AbstractOwlRdfConverter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java b/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java index 34ff4c00..193caeab 100644 --- a/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java +++ b/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java @@ -34,15 +34,12 @@ import org.apache.jena.ontapi.model.OntID; import org.apache.jena.ontapi.model.OntIndividual; import org.apache.jena.ontapi.model.OntModel; -import org.apache.jena.ontapi.model.OntObject; import org.apache.jena.ontapi.model.OntProperty; -import org.apache.jena.rdf.model.NodeIterator; import org.apache.jena.rdf.model.Property; import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.Statement; import org.apache.jena.rdf.model.StmtIterator; -import org.apache.jena.util.iterator.ExtendedIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spdx.library.model.v2.SpdxConstantsCompatV2; From 487d1ec4f67a6b0c1980eb4a94a2301cd2db1199 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 20 Jul 2026 11:05:19 +0100 Subject: [PATCH 3/3] Add OWL->JSON context / schema tests Signed-off-by: Arthit Suriyawongkul --- .../tools/schema/AbstractOwlRdfConverter.java | 57 +++++++++++++----- .../spdx/tools/schema/OwlToJsonSchema.java | 7 ++- .../java/org/spdx/tools/schema/OwlToXsd.java | 18 ++++-- .../tools/schema/OwlToJsonContextTest.java | 46 +++++++++++++++ .../tools/schema/OwlToJsonSchemaTest.java | 59 +++++++++++++++++++ .../org/spdx/tools/schema/OwlToXSDTest.java | 3 + 6 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 src/test/java/org/spdx/tools/schema/OwlToJsonContextTest.java create mode 100644 src/test/java/org/spdx/tools/schema/OwlToJsonSchemaTest.java diff --git a/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java b/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java index 193caeab..778b815d 100644 --- a/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java +++ b/src/main/java/org/spdx/tools/schema/AbstractOwlRdfConverter.java @@ -32,7 +32,6 @@ import org.apache.jena.ontapi.model.OntClass; import org.apache.jena.ontapi.model.OntID; -import org.apache.jena.ontapi.model.OntIndividual; import org.apache.jena.ontapi.model.OntModel; import org.apache.jena.ontapi.model.OntProperty; import org.apache.jena.rdf.model.Property; @@ -101,7 +100,8 @@ public PropertyRestrictions(OntClass ontClass, OntProperty property) { Objects.requireNonNull(ontClass, "Missing required ontology class"); Objects.requireNonNull(property, "Missing required property"); this.property = property; - List restrictions = getRestrictionsFromSuperclasses(ontClass, property); + List restrictions = + getRestrictionsFromSuperclasses(ontClass, property); interpretRestrictions(restrictions); } @@ -146,22 +146,28 @@ private void interpretRestrictions(List restrictions) { } // cardinality Statement qualCardStmt = r.getProperty(qualCardProperty); - RDFNode qualCardPropValue = qualCardStmt != null ? qualCardStmt.getObject() : null; + RDFNode qualCardPropValue = qualCardStmt != null + ? qualCardStmt.getObject() : null; Statement cardStmt = r.getProperty(cardProperty); - RDFNode cardPropValue = cardStmt != null ? cardStmt.getObject() : null; + RDFNode cardPropValue = cardStmt != null + ? cardStmt.getObject() : null; Statement maxQualCardStmt = r.getProperty(maxQualCardProperty); - RDFNode maxQualCardPropValue = maxQualCardStmt != null ? maxQualCardStmt.getObject() : null; + RDFNode maxQualCardPropValue = maxQualCardStmt != null + ? maxQualCardStmt.getObject() : null; Statement maxCardStmt = r.getProperty(maxCardProperty); - RDFNode maxCardPropValue = maxCardStmt != null ? maxCardStmt.getObject() : null; + RDFNode maxCardPropValue = maxCardStmt != null + ? maxCardStmt.getObject() : null; Statement minCardStmt = r.getProperty(minCardProperty); - RDFNode minCardPropValue = minCardStmt != null ? minCardStmt.getObject() : null; + RDFNode minCardPropValue = minCardStmt != null + ? minCardStmt.getObject() : null; Statement minQualCardStmt = r.getProperty(minQualCardProperty); - RDFNode minQualCardPropValue = minQualCardStmt != null ? minQualCardStmt.getObject() : null; + RDFNode minQualCardPropValue = minQualCardStmt != null + ? minQualCardStmt.getObject() : null; if (Objects.nonNull(qualCardPropValue) && qualCardPropValue.isLiteral()) { absoluteCardinality = qualCardPropValue.asLiteral().getInt(); @@ -221,24 +227,40 @@ private void interpretRestrictions(List restrictions) { } }); } + if (Objects.isNull(typeUri)) { + try { + Optional propType = getPropertyType(property); + if (propType.isPresent()) { + typeUri = propType.get().getURI(); + } + } catch (Exception e) { + logger.warn("Error getting property type for " + + property.getLocalName(), e); + } + } if (Objects.isNull(typeUri) && ("comment".equals(property.getLocalName()) || "seeAlso".equals(property.getLocalName()))) { // A bit of a hack, the schema file can not store the type of rdfs:comment, so we must override it to xsd:string typeUri = SpdxConstantsCompatV2.XML_SCHEMA_NAMESPACE + "string"; } } - private List getRestrictionsFromSuperclasses(OntClass ontClass, OntProperty property) { + private List getRestrictionsFromSuperclasses( + OntClass ontClass, OntProperty property) { List retval = new ArrayList<>(); ontClass.superClasses(false).forEach(superClass -> { if (superClass instanceof OntClass.UnionOf) { OntClass.UnionOf uClass = (OntClass.UnionOf) superClass; uClass.components().forEach(operand -> { - if (operand instanceof OntClass.UnaryRestriction && property.equals(((OntClass.UnaryRestriction) operand).getProperty())) { + if (operand instanceof OntClass.UnaryRestriction + && property.equals( + ((OntClass.UnaryRestriction) operand) + .getProperty())) { retval.add((OntClass.Restriction) operand); } }); } else if (superClass instanceof OntClass.UnaryRestriction) { - OntClass.UnaryRestriction r = (OntClass.UnaryRestriction) superClass; + OntClass.UnaryRestriction r = + (OntClass.UnaryRestriction) superClass; if (property.equals(r.getProperty())) { retval.add(r); } @@ -252,8 +274,8 @@ public PropertyRestrictions(OntProperty property) { this.property = property; List propertyRestrictions = new ArrayList<>(); if (property instanceof org.apache.jena.ontapi.model.OntRelationalProperty) { - ((org.apache.jena.ontapi.model.OntRelationalProperty) property).referringRestrictions() - .forEach(propertyRestrictions::add); + ((org.apache.jena.ontapi.model.OntRelationalProperty) property) + .referringRestrictions().forEach(propertyRestrictions::add); } interpretRestrictions(propertyRestrictions); } @@ -436,7 +458,8 @@ protected Collection propertiesFromClassRestrictions(OntClass oClas * @throws SchemaException */ protected Optional getPropertyType(OntProperty property) throws SchemaException { - java.util.Iterator rangeIter = property.ranges().iterator(); + java.util.Iterator rangeIter = + property.ranges().iterator(); while (rangeIter.hasNext()) { Resource range = rangeIter.next(); if (range.isURIResource()) { @@ -505,7 +528,8 @@ private void collectPropertiesFromRestrictions(OntClass oClass, properties.add(property); } } else { - java.util.stream.Stream superClassStream = oClass.superClasses(excludeSuperClassProperties); + java.util.stream.Stream superClassStream = + oClass.superClasses(excludeSuperClassProperties); if (excludeSuperClassProperties) { superClassStream = superClassStream.filter(sc -> { return !sc.isURIResource() || @@ -513,7 +537,8 @@ private void collectPropertiesFromRestrictions(OntClass oClass, }); } superClassStream.forEach(sc -> { - collectPropertiesFromRestrictions(sc, properties, reviewedClasses, excludeSuperClassProperties); + collectPropertiesFromRestrictions(sc, properties, + reviewedClasses, excludeSuperClassProperties); }); } } diff --git a/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java b/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java index 110703f8..ac157e61 100644 --- a/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java +++ b/src/main/java/org/spdx/tools/schema/OwlToJsonSchema.java @@ -93,7 +93,8 @@ public ObjectNode convertToJsonSchema() { String version = null; if (ont != null && ont.isURIResource()) { version = ont.getVersionInfo(); - String ontologyUri = version == null ? ont.getURI() : ont.getURI() + "/" + version; + String ontologyUri = version == null ? ont.getURI() + : ont.getURI() + "/" + version; if (Objects.nonNull(ontologyUri)) { root.put("$id", ontologyUri); } @@ -125,7 +126,9 @@ public ObjectNode convertToJsonSchema() { ObjectNode describesProperty = toArraySchema(createSimpleTypeSchema(JSON_TYPE_STRING, "SPDX ID for each Package, File, or Snippet."), "DEPRECATED: use relationships instead of this field. Packages, files and/or Snippets described by this SPDX document", 0); describesProperty.put("deprecated", true); - describesProperty.put("$comment", "This field has been deprecated as it is a duplicate of using the SPDXRef-DOCUMENT DESCRIBES relationship"); + describesProperty.put("$comment", + "This field has been deprecated as it is a duplicate of " + + "using the SPDXRef-DOCUMENT DESCRIBES relationship"); properties.set(SpdxConstantsCompatV2.PROP_DOCUMENT_DESCRIBES.getName(), describesProperty); OntClass packageClass = model.getOntClass(SpdxConstantsCompatV2.SPDX_NAMESPACE + SpdxConstantsCompatV2.CLASS_SPDX_PACKAGE); diff --git a/src/main/java/org/spdx/tools/schema/OwlToXsd.java b/src/main/java/org/spdx/tools/schema/OwlToXsd.java index 7513bf91..0063eb32 100644 --- a/src/main/java/org/spdx/tools/schema/OwlToXsd.java +++ b/src/main/java/org/spdx/tools/schema/OwlToXsd.java @@ -83,7 +83,8 @@ public XmlSchema convertToXsd() throws XmlSchemaSerializerException, SchemaExcep schema.setNamespaceContext(namespaces); addDocumentation(schema, schema, ontology.getComment(null)); - java.util.List classes = model.classes().collect(java.util.stream.Collectors.toList()); + java.util.List classes = model.classes() + .collect(java.util.stream.Collectors.toList()); for (OntClass.Named type : classes) { addTypeToSchema(schema, type); } @@ -91,13 +92,17 @@ public XmlSchema convertToXsd() throws XmlSchemaSerializerException, SchemaExcep // Add the top level element XmlSchemaElement documentElement = new XmlSchemaElement(schema, true); documentElement.setName("Document"); - documentElement.setSchemaTypeName(new QName(SpdxConstantsCompatV2.SPDX_NAMESPACE.substring(0, SpdxConstantsCompatV2.SPDX_NAMESPACE.length()-1), "SpdxDocument")); + documentElement.setSchemaTypeName(new QName( + SpdxConstantsCompatV2.SPDX_NAMESPACE.substring(0, + SpdxConstantsCompatV2.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 { - java.util.List individuals = type.individuals().collect(java.util.stream.Collectors.toList()); + java.util.List individuals = type.individuals() + .collect(java.util.stream.Collectors.toList()); if (!individuals.isEmpty()) { // Enum type addEnumTypeToSchema(schema, type, individuals); @@ -112,7 +117,8 @@ private XmlSchemaType addComplexTypeToSchema(XmlSchema schema, OntClass type) th addDocumentation(schema, xmlType, type.getComment(null)); XmlSchemaComplexContentExtension schemaExtension = null; java.util.List superClasses = type.superClasses(true) - .filter(sc -> sc.isURIResource() && !"http://www.w3.org/2000/01/rdf-schema#Container".equals(sc.getURI())) + .filter(sc -> sc.isURIResource() + && !"http://www.w3.org/2000/01/rdf-schema#Container".equals(sc.getURI())) .collect(java.util.stream.Collectors.toList()); if (!superClasses.isEmpty()) { @@ -189,7 +195,9 @@ private XmlSchemaType addComplexTypeToSchema(XmlSchema schema, OntClass type) th return xmlType; } - private void addEnumTypeToSchema(XmlSchema schema, OntClass type, List individuals) throws XmlSchemaSerializerException { + 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)); diff --git a/src/test/java/org/spdx/tools/schema/OwlToJsonContextTest.java b/src/test/java/org/spdx/tools/schema/OwlToJsonContextTest.java new file mode 100644 index 00000000..909932fc --- /dev/null +++ b/src/test/java/org/spdx/tools/schema/OwlToJsonContextTest.java @@ -0,0 +1,46 @@ +/** + * SPDX-FileCopyrightText: 2026 SPDX Contributors + * SPDX-FileType: SOURCE + * SPDX-License-Identifier: Apache-2.0 + */ +package org.spdx.tools.schema; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +import junit.framework.TestCase; + +/** + * Tests generation of JSON-LD contexts from SPDX OWL ontologies. + */ +public class OwlToJsonContextTest extends TestCase { + + static final String OWL_FILE_PATH = "testResources" + File.separator + + "spdx-2-2-revision-8-ontology.owl.xml"; + + public void testConvertToContext() throws IOException { + OwlToJsonContext otjc = null; + try (InputStream is = new FileInputStream(new File(OWL_FILE_PATH))) { + OntModel model = OntModelFactory + .createModel(OntSpecification.OWL2_DL_MEM); + model.read(is, "RDF/XML"); + otjc = new OwlToJsonContext(model); + } + ObjectNode result = otjc.convertToContext(); + + assertNotNull(result); + assertTrue(result.has("@context")); + ObjectNode context = (ObjectNode) result.get("@context"); + + assertTrue(context.size() > 0); + assertTrue(context.has("Document")); + } +} diff --git a/src/test/java/org/spdx/tools/schema/OwlToJsonSchemaTest.java b/src/test/java/org/spdx/tools/schema/OwlToJsonSchemaTest.java new file mode 100644 index 00000000..c8b28d68 --- /dev/null +++ b/src/test/java/org/spdx/tools/schema/OwlToJsonSchemaTest.java @@ -0,0 +1,59 @@ +/** + * SPDX-FileCopyrightText: 2026 SPDX Contributors + * SPDX-FileType: SOURCE + * SPDX-License-Identifier: Apache-2.0 + */ +package org.spdx.tools.schema; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.jena.ontapi.OntModelFactory; +import org.apache.jena.ontapi.OntSpecification; +import org.apache.jena.ontapi.model.OntModel; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import junit.framework.TestCase; + +/** + * Tests generation of JSON-Schema definitions from SPDX OWL ontologies. + */ +public class OwlToJsonSchemaTest extends TestCase { + + static final String OWL_FILE_PATH = "testResources" + File.separator + + "spdx-2-2-revision-8-ontology.owl.xml"; + + public void testConvertToJsonSchema() throws IOException { + OwlToJsonSchema otjs = null; + try (InputStream is = new FileInputStream(new File(OWL_FILE_PATH))) { + OntModel model = OntModelFactory + .createModel(OntSpecification.OWL2_DL_MEM); + model.read(is, "RDF/XML"); + otjs = new OwlToJsonSchema(model); + } + ObjectNode result = otjs.convertToJsonSchema(); + + assertNotNull(result); + assertTrue(result.has("$schema")); + assertEquals("https://json-schema.org/draft/2019-09/schema#", + result.get("$schema").asText()); + assertTrue(result.has("type")); + assertEquals("object", result.get("type").asText()); + + assertTrue(result.has("properties")); + ObjectNode properties = (ObjectNode) result.get("properties"); + + assertTrue(properties.has("packages")); + assertTrue(properties.has("files")); + assertTrue(properties.has("snippets")); + assertTrue(properties.has("relationships")); + + assertTrue(result.has("required")); + ArrayNode required = (ArrayNode) result.get("required"); + assertTrue(required.size() > 0); + } +} diff --git a/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java b/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java index 4d71409d..8a699881 100644 --- a/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java +++ b/src/test/java/org/spdx/tools/schema/OwlToXSDTest.java @@ -18,6 +18,9 @@ import junit.framework.TestCase; +/** + * Tests generation of XML Schema Definitions (XSD) from SPDX OWL ontologies. + */ public class OwlToXSDTest extends TestCase { static final String OWL_FILE_PATH = "testResources" + File.separator + "spdx-2-2-revision-8-ontology.owl.xml";