Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/enforce full closure controls missing parent exception #4

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ public abstract class AbstractOntology implements ClosureIndex {
protected final Model closureModel = MODEL_FACTORY.createEmptyModel();
protected final SourceGenerator sourceGenerator;
protected final JCodeModel codeModel;
protected final boolean enforceFullClosure;

public AbstractOntology(SourceGenerator sourceGenerator, JCodeModel codeModel) {
protected AbstractOntology(SourceGenerator sourceGenerator, JCodeModel codeModel, boolean enforceFullClosure) {
this.sourceGenerator = sourceGenerator;
this.codeModel = codeModel;
this.enforceFullClosure = enforceFullClosure;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@
import com.realmone.owl.orm.generate.properties.ObjectProperty;
import com.realmone.owl.orm.generate.support.GraphUtils;
import com.realmone.owl.orm.generate.support.NamingUtilities;
import com.sun.codemodel.*;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocComment;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JPackage;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
Expand All @@ -32,8 +39,10 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

@Slf4j
@Getter
public class GeneratingOntology extends AbstractOntology {

Expand All @@ -55,7 +64,8 @@ public GeneratingOntology(@NonNull JCodeModel codeModel, @NonNull Model ontology
@NonNull Model referenceModel, @NonNull String ontologyName,
@NonNull String ontologyPackage, @NonNull SourceGenerator sourceGenerator,
boolean enforceFullClosure) throws OrmException {
super(sourceGenerator, codeModel);
super(sourceGenerator, codeModel, enforceFullClosure);

this.jPackage = codeModel._package(ontologyPackage);
this.model = ontologyModel;
this.ontologyName = ontologyName;
Expand Down Expand Up @@ -108,18 +118,28 @@ private void analyzeAndGenerate() throws OrmException {
classIris.forEach(classResource -> {
classIndex.put(classResource, generateInterface(classResource));
// Add our class to the hierarchy
classHierarchy.put(classResource, GraphUtils.lookupParentClasses(closureModel, classResource));
classHierarchy.put(classResource, GraphUtils.lookupParentClasses(closureModel, classResource,
enforceFullClosure));
});
// Define the class hierarchy
classHierarchy.forEach((classResource, parents) -> {
try {
JDefinedClass clazz = (JDefinedClass) classIndex.get(classResource);
parents.forEach(parentResource -> {
JClass ref = findClassReference(parentResource)
//TODO - better error message
.orElseThrow(() -> new OrmGenerationException("Couldn't find parent class in index: " +
parentResource));
clazz._implements(ref);
Optional<JClass> optionalParent = findClassReference(parentResource);
// If the parent class is present in the closure
if (optionalParent.isPresent()) {
clazz._implements(optionalParent.get());
}
// Else if we're configured to enforce the full closure we should throw an exception
else if (enforceFullClosure) {
throw new OrmGenerationException("Couldn't find parent class in index: " + parentResource);
}
// Else we should log a warning to the user, and trust they know what they're doing :)
else {
log.warn("Couldn't find parent class of '{}' in closure: {}", classResource.stringValue(),
parentResource.stringValue());
}
});
} catch (ClassCastException e) {
//TODO - better error handling...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
*/
package com.realmone.owl.orm.generate;

import lombok.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

@Data
@AllArgsConstructor(access = AccessLevel.PRIVATE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class ReferenceOntology extends AbstractOntology {
protected ReferenceOntology(@NonNull JCodeModel codeModel, @NonNull Model ontologyModel,
@NonNull String packageName, @NonNull String ontologyName,
@NonNull SourceGenerator sourceGenerator) throws OrmException {
super(sourceGenerator, codeModel);
super(sourceGenerator, codeModel, false);
final Set<Resource> ontologiesInModel = ontologyModel.filter(null, RDF.TYPE, OWL.ONTOLOGY).subjects();
if (ontologiesInModel.size() > 1) {
throw new OrmException("Ontology data contains multiple ontology definitions");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ protected DatatypeProperty(Resource rangeIri, Set<Resource> domains, JCodeModel

@Override
public void additionalAttach(JDefinedClass jDefinedClass) throws OrmGenerationException {


// Intentionally left empty for this implementation of Property.
}

private static JClass identifyRange(JCodeModel codeModel, Resource rangeIri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected ObjectProperty(Resource rangeResource, Set<Resource> domains, ClosureI

@Override
public void additionalAttach(JDefinedClass jDefinedClass) throws OrmGenerationException {

// Intentionally left empty for this implementation of Property
}

private static JClass identifyRange(ClosureIndex closureIndex, Resource rangeIri, JCodeModel codeModel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@

import com.realmone.owl.orm.generate.ClosureIndex;
import com.realmone.owl.orm.generate.OrmGenerationException;
import com.sun.codemodel.*;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocComment;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JVar;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.google.common.xml.XmlEscapers;
import com.realmone.owl.orm.generate.OrmGenerationException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Value;
Expand All @@ -26,6 +27,7 @@
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@UtilityClass
public class GraphUtils {

Expand Down Expand Up @@ -59,16 +61,21 @@ public static Set<Resource> lookupDomain(Model model, Resource propertyResource)
}
}

public static Set<Resource> lookupParentClasses(Model model, Resource classResource) throws OrmGenerationException {
public static Set<Resource> lookupParentClasses(Model model, Resource classResource, boolean enforceFullClosure)
throws OrmGenerationException {
try {
return model.filter(classResource, RDFS.SUBCLASSOF, null)
// Find the subClassOf properties
.objects().stream().map(Resource.class::cast)
.filter(parentResource -> {
if (model.filter(parentResource, null, null).isEmpty()) {
//TODO improve error message to include ontology, child class, and missing parent.
throw new OrmGenerationException("No ontology data about parent resource in model for " +
classResource.stringValue() + ": " + parentResource.stringValue());
String message = String.format("Searching for a parent of class %s, and couldn't find the parent class of %s",
classResource.stringValue(), parentResource.stringValue());
if (enforceFullClosure) {
throw new OrmGenerationException(message);
} else {
log.warn(message);
}
}
return !model.filter(parentResource, RDF.TYPE, OWL.CLASS).isEmpty();
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,38 @@ public void testMissingClosure() {
.build();
}

@Test(expected = OrmGenerationException.class)
public void testMissingParentClass() {
SourceGenerator.builder()
.enforceFullClosure(true)
.outputLocation("target/")
.generateForOntologies(Set.of(
OntologyMeta.builder()
.ontologyName("BierOnto")
.packageName("com.realmone.bieronto")
.file(new File("src/test/resources/BierOntoWithoutImport.ttl")
.getAbsolutePath())
.build()))
.referenceOntologies(Collections.emptySet())
.build();
}

@Test
public void testMissingParentClassAllowed() {
SourceGenerator.builder()
.enforceFullClosure(false)
.outputLocation("target/source-gen-test-allowed-missing")
.generateForOntologies(Set.of(
OntologyMeta.builder()
.ontologyName("BierOnto")
.packageName("com.realmone.bieronto")
.file(new File("src/test/resources/BierOntoWithoutImport.ttl")
.getAbsolutePath())
.build()))
.referenceOntologies(Collections.emptySet())
.build();
}

private Set<OntologyMeta> singletonSet(OntologyMeta wrapper) {
return Collections.singleton(wrapper);
}
Expand Down
Loading