com.github.VisualDataWeb
OWL2VOWL
diff --git a/src/main/java/widoco/Constants.java b/src/main/java/widoco/Constants.java
index c569c1d5..9f9088ad 100644
--- a/src/main/java/widoco/Constants.java
+++ b/src/main/java/widoco/Constants.java
@@ -25,6 +25,7 @@
import widoco.entities.Agent;
import widoco.entities.Ontology;
+import widoco.ExternalPropertyParser;
/**
*
@@ -337,6 +338,7 @@ public class Constants {
public static final String LANG_CLASSES = "classes";
public static final String LANG_OBJ_PROP = "objProp";
public static final String LANG_DATA_PROP = "dataProp";
+ public static final String LANG_EXT_PROP = "extProp";
public static final String LANG_ANN_PROP = "annProp";
public static final String LANG_NAMED_INDIV = "namedIndiv";
public static final String LANG_TABLE_OF_CONTENTS = "tableOfContents";
@@ -1510,25 +1512,30 @@ public static String getLegend(
+ " " + lang.getProperty(Constants.LANG_BACK)
+ " ToC\n"
+ "\n"
- + (includesClass ?
+ + (includesClass || ExternalPropertyParser.hasClasses()?
"c: " + lang.getProperty(Constants.LANG_CLASSES) + "
\n"
: "")
- + (includesProperty ?
+ + (includesProperty || ExternalPropertyParser.hasObjProps()?
"op: "
+ lang.getProperty(Constants.LANG_OBJ_PROP) + "
\n"
: "")
- + (includesDatatypeProperty ?
+ + (includesDatatypeProperty || ExternalPropertyParser.hasDataProps()?
"dp: "
+ lang.getProperty(Constants.LANG_DATA_PROP) + "
\n"
: "")
- + (includesNamedIndividual ?
+ + (includesNamedIndividual || ExternalPropertyParser.hasNamedIndiv() ?
"ni: "
- + lang.getProperty(Constants.LANG_NAMED_INDIV) + "\n"
+ + lang.getProperty(Constants.LANG_NAMED_INDIV) + "
\n"
+ : "")
+ + (ExternalPropertyParser.hasExternalProps() ?
+ "ep: "
+ + lang.getProperty(Constants.LANG_EXT_PROP) + "\n"
: "")
+ "
\n" + ""
+ "\n";
diff --git a/src/main/java/widoco/CreateResources.java b/src/main/java/widoco/CreateResources.java
index ecb34925..cc197798 100644
--- a/src/main/java/widoco/CreateResources.java
+++ b/src/main/java/widoco/CreateResources.java
@@ -70,7 +70,7 @@ public static void generateDocumentation(String outFolder, Configuration c, File
logger.info("- ontology IRI: " + c.getOntologyURI());
lodeContent = LODEGeneration.getLODEhtml(c, lodeResources);
LODEParser lode = new LODEParser(lodeContent, c, languageFile);
-
+
if (c.isCreateHTACCESS()) {
File fOut = new File(folderOut);
if (!fOut.exists()) {
diff --git a/src/main/java/widoco/ExternalPropertyParser.java b/src/main/java/widoco/ExternalPropertyParser.java
new file mode 100644
index 00000000..38380f67
--- /dev/null
+++ b/src/main/java/widoco/ExternalPropertyParser.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2024 Victor Chavez
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package widoco;
+
+import org.semanticweb.owlapi.model.*;
+import org.semanticweb.owlapi.model.parameters.Imports;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.semanticweb.owlapi.search.EntitySearcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+public class ExternalPropertyParser {
+ private enum PropertyType {
+ OBJECT_PROPERTY,
+ DATA_PROPERTY,
+ NAMED_INDIVIDUAL,
+ CLASS,
+ ANNOTATION_PROPERTY,
+ EXTERNAL_PROPERTY,
+ }
+
+ private OWLOntology ontology;
+ private static boolean uses_ep = false;
+ private static boolean uses_op = false;
+ private static boolean uses_ap = false;
+ private static boolean uses_dp = false;
+ private static boolean uses_ni = false;
+ private static boolean uses_c = false;
+
+ private Properties lang;
+
+ private final static String OP_PROP = "objectproperty";
+ private final static String DP_PROP = "dataproperty";
+ private final static String CLASS_PROP = "class";
+ private final static String NI_PROP = "namedindividual";
+ private final static String AP_PROP = "annotationproperty";
+ private final static String EP_PROP = "externalproperty";
+
+ private String ontologyNSURI = "";
+ private String langPrefix ="";
+
+
+ private static final Logger logger = LoggerFactory.getLogger(ExternalPropertyParser.class);
+
+ public static boolean hasExternalProps(){
+ return uses_ep;
+ }
+
+ public static boolean hasClasses(){
+ return uses_c;
+ }
+
+ public static boolean hasObjProps(){
+ return uses_op;
+ }
+
+ public static boolean hasAnnotProps(){
+ return uses_ap;
+ }
+
+ public static boolean hasDataProps(){
+ return uses_dp;
+ }
+
+ public static boolean hasNamedIndiv(){
+ return uses_ni;
+ }
+
+ public void setOntology(OWLOntology ont,String uri) {
+ ontology = ont;
+ ontologyNSURI = uri;
+ }
+
+ /**
+ * Set the language for the external parser.
+ * Load the lode/XX.xml resource with the specific language
+ * and save it to a Property object
+ * @param lang
+ */
+ public void setLang(String lang) {
+ String resource = "/lode/"+lang+".xml";
+ langPrefix = lang;
+ Properties properties = new Properties();
+ try (InputStream inputStream = ExternalPropertyParser.class.getResourceAsStream(resource)) {
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ org.w3c.dom.Document document = dBuilder.parse(inputStream);
+ document.getDocumentElement().normalize();
+ // Get all elements inside
+ org.w3c.dom.NodeList nodeList = document.getElementsByTagName("labels");
+ // Iterate through each element
+ for (int temp = 0; temp < nodeList.getLength(); temp++) {
+ org.w3c.dom.Node node = nodeList.item(temp);
+ if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
+ org.w3c.dom.Element element = (org.w3c.dom.Element) node;
+ // Get all child nodes (tags) inside
+ org.w3c.dom.NodeList childNodes = element.getChildNodes();
+ // Iterate through each child node
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ org.w3c.dom.Node childNode = childNodes.item(i);
+ if (childNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
+ // Convert the tag name and text content into Properties
+ properties.setProperty(childNode.getNodeName(), childNode.getTextContent());
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ this.lang = null;
+ }
+ this.lang = properties;
+ }
+
+ /**
+ * Parse html content with external property tags, i.e., the sup tag
+ * with class type-ep and attempt to find the type of property type
+ * within the ontology.
+ * The main purpose of this utility is to add metadata from the ontology
+ * since the xslt transform cannot look for external properties
+ * outisde of the xml rdf serialization of the ontology when Widoco
+ * does not document the imported ontologies.
+ * @param htmlContent Html content where the external properties are located.
+ * Typically this is the named individuals section
+ * @return
+ */
+ public String parse(String htmlContent) {
+
+ Document document = Jsoup.parseBodyFragment(htmlContent, "UTF-8");
+ // Find all tags with tags having class "type-ep"
+ Elements superscripts = document.select("a + sup.type-ep");
+ // Modify the tag based on the property type
+ for (Element superscript : superscripts) {
+ Element owlObjTextRef = superscript.previousElementSibling();
+ String href_val = owlObjTextRef.attr("href");
+ boolean hrefToHash = href_val.startsWith("#");
+ String search_iri;
+ if (hrefToHash) {
+ search_iri = owlObjTextRef.attr("title");
+ } else {
+ search_iri = href_val;
+ }
+ PropertyType type = getPropertyType(search_iri);
+ String class_name;
+ String text;
+ String title;
+ if (type == PropertyType.OBJECT_PROPERTY) {
+ class_name = "type-op";
+ text="op";
+ if(lang != null) {
+ title = lang.getProperty(OP_PROP);
+ } else {
+ title = "object property";
+ }
+ if (!uses_op) {
+ uses_op = true;
+ }
+ } else if(type == PropertyType.DATA_PROPERTY) {
+ class_name = "type-dp";
+ text="dp";
+ if (!uses_dp) {
+ uses_dp = true;
+ }
+ if(lang != null) {
+ title = lang.getProperty(DP_PROP);
+ } else {
+ title = "data property";
+ }
+ } else if (type == PropertyType.NAMED_INDIVIDUAL) {
+ class_name = "type-ni";
+ text="ni";
+ if (!uses_ni) {
+ uses_ni = true;
+ }
+ if(lang != null) {
+ title = lang.getProperty(NI_PROP);
+ } else {
+ title = "named individual";
+ }
+ } else if (type == PropertyType.CLASS) {
+ class_name = "type-c";
+ text="c";
+ if (!uses_c) {
+ uses_c = true;
+ }
+ if(lang != null) {
+ title = lang.getProperty(CLASS_PROP);
+ } else {
+ title = "class";
+ }
+ } else if (type == PropertyType.ANNOTATION_PROPERTY) {
+ class_name = "type-ap";
+ text="ap";
+ if (!uses_ap) {
+ uses_ap = true;
+ }
+ if(lang != null) {
+ title = lang.getProperty(AP_PROP);
+ } else {
+ title = "annotation property";
+ }
+ } else {
+ class_name = "type-ep";
+ text ="ep";
+ if (!uses_ep) {
+ uses_ep = true;
+ }
+ if(lang != null) {
+ title = lang.getProperty(EP_PROP);
+ } else {
+ title = "external property";
+ }
+ }
+ if (!hrefToHash) {
+ // In case the iri belongs to the ontology but
+ // through xslt transformation it did not get
+ // referenced properly
+ if (href_val.contains(ontologyNSURI)) {
+ // remove the "target="_blank" since the IRI is in
+ // the ontology
+ String newRef = href_val.replace(ontologyNSURI,"");
+ owlObjTextRef.removeAttr("target");
+ owlObjTextRef.attr("href",newRef);
+ }
+ }
+ if (type != PropertyType.EXTERNAL_PROPERTY) {
+ String label = findLabel(search_iri,langPrefix);
+ if (label != null) {
+ owlObjTextRef.text(label);
+ }
+ }
+ superscript.text(text);
+ superscript.attr("class",class_name);
+ superscript.attr("title",title);
+ }
+ return document.body().html();
+ }
+
+ /**
+ * Find a label for a External Property from
+ * the loaded ontology
+ * @param obj_iri IRI whose rdfs:label will be searched
+ * @param langPrefix Preffered language of the label, if not found
+ * it will try to find any label.
+ * @return
+ */
+ private String findLabel(String obj_iri, String langPrefix) {
+ IRI iri = IRI.create(obj_iri);
+ OWLEntity entity = ontology.entitiesInSignature(iri).findFirst().orElse(null);
+ if (entity != null) {
+ java.util.Optional labelAnnotation = getLabelAnnotation(entity, langPrefix);
+
+ if (labelAnnotation.isPresent()) {
+ return labelAnnotation.get().getValue().asLiteral().get().getLiteral();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the rdfs:label in the form of OWLAnnotation
+ * This will look in the main ontology and also in the imported ontologies
+ * @param entity OWLEntity whose label will be searched
+ * @param langPrefix Preferred language of the label
+ * @return
+ */
+ private Optional getLabelAnnotation(OWLEntity entity, String langPrefix) {
+ Optional label = getLabel(entity, langPrefix, ontology);
+ if (!label.isEmpty()) return label;
+
+ // Check imported ontologies
+ for (OWLOntology importedOntology : ontology.getImports()) {
+ String importedIRI = importedOntology.getOntologyID().getOntologyIRI().get().toString();
+ if (!entity.getIRI().toString().contains(importedIRI)) {
+ continue;
+ }
+ label = getLabel(entity, langPrefix, importedOntology);
+ if (label != null) return label;
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Search for an rdfs:label based on the language preference and an ontology
+ * @param entity OWLEntity whose rdfs:label will be searched
+ * @param langPrefix Preferred language
+ * @param ontology Ontology used as reference for the search
+ * @return OWLAnnotation with the rdfs:label
+ */
+ private Optional getLabel(OWLEntity entity, String langPrefix, OWLOntology ontology) {
+ List annotationList = EntitySearcher.getAnnotations(entity, ontology).collect(Collectors.toList());
+
+ Predicate isValidLabel = annotation ->
+ annotation.getProperty().isLabel() &&
+ annotation.getValue().asLiteral().isPresent();
+
+ Optional labelWithLang = annotationList.stream()
+ .filter(isValidLabel.and(annotation ->
+ annotation.getValue().asLiteral().get().getLang().equals(langPrefix)))
+ .findFirst();
+
+ if (labelWithLang.isPresent()) {
+ return labelWithLang; // Found the annotation with the specified langPrefix
+ }
+
+ // If langPrefix is not found, try to find the annotation with langPrefix = ""
+ Optional labelWithEmptyLang = annotationList.stream()
+ .filter(isValidLabel.and(annotation ->
+ annotation.getValue().asLiteral().get().getLang().isEmpty()))
+ .findFirst();
+
+ if (labelWithEmptyLang.isPresent()) {
+ return labelWithEmptyLang; // Found the annotation with langPrefix = ""
+ }
+
+ // If neither langPrefix nor "" is found, return the first available label
+ return annotationList.stream()
+ .filter(isValidLabel)
+ .findFirst();
+ }
+
+ /**
+ * Find the type of property an owl entity marked as external property
+ * with the xslt transformation
+ * @param entityIRI IRI of the owl entity to look for
+ * @return Property type, if not found it will return PropertyType.propertyIRI
+ */
+ private PropertyType getPropertyType(String entityIRI) {
+ IRI propertyIRIObject = IRI.create(entityIRI);
+ Set objectProperties = ontology.getObjectPropertiesInSignature(Imports.INCLUDED);
+ for (OWLObjectProperty objectProperty : objectProperties) {
+ if (objectProperty.getIRI().equals(propertyIRIObject)) {
+ return PropertyType.OBJECT_PROPERTY;
+ }
+ }
+ Set dataProperties = ontology.getDataPropertiesInSignature(Imports.INCLUDED);
+ for (OWLDataProperty dataProperty : dataProperties) {
+ if (dataProperty.getIRI().equals(propertyIRIObject)) {
+ return PropertyType.DATA_PROPERTY;
+ }
+ }
+ Set individuals = ontology.getIndividualsInSignature(Imports.INCLUDED);
+ for (OWLNamedIndividual individual : individuals) {
+ if (individual.getIRI().equals(propertyIRIObject)) {
+ return PropertyType.NAMED_INDIVIDUAL;
+ }
+ }
+ Set classes = ontology.getClassesInSignature(Imports.INCLUDED);
+ for (OWLClass owl_class : classes) {
+ if (owl_class.getIRI().equals(propertyIRIObject)) {
+ return PropertyType.CLASS;
+ }
+ }
+ Set annotationProperties = ontology.getAnnotationPropertiesInSignature(Imports.INCLUDED);
+ for (OWLAnnotationProperty annotationProperty : annotationProperties) {
+ if (annotationProperty.getIRI().equals(propertyIRIObject)) {
+ return PropertyType.ANNOTATION_PROPERTY;
+ }
+ }
+ return PropertyType.EXTERNAL_PROPERTY;
+ }
+
+}
diff --git a/src/main/java/widoco/LODEParser.java b/src/main/java/widoco/LODEParser.java
index 2c583158..f5056477 100644
--- a/src/main/java/widoco/LODEParser.java
+++ b/src/main/java/widoco/LODEParser.java
@@ -41,6 +41,8 @@
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
+import widoco.ExternalPropertyParser;
+
/**
* Class made for parsing and manipulating LODE's html. This class contains most
* of the TemplateGeneratorOLD class
@@ -69,6 +71,7 @@ public class LODEParser {
private String swrlrules;
private String swrlruleslist;
Configuration c;
+ ExternalPropertyParser extParser;
/**
* Constructor for the LODE parser. The reason for creating this class is to reuse certain parts of
@@ -84,6 +87,9 @@ public class LODEParser {
public LODEParser(String lodeContent, Configuration c, Properties langFile) {
replacements = new HashMap();
this.c = c;
+ extParser = new ExternalPropertyParser();
+ extParser.setOntology(c.getMainOntology().getOWLAPIModel(),c.getMainOntology().getNamespaceURI());
+ extParser.setLang(c.getCurrentLanguage());
parse(lodeContent, langFile);
//System.out.println(lodeContent);
}
@@ -190,6 +196,7 @@ private void parse(String content, Properties langFile) {
classes = classes.replace("" + langFile.getProperty(Constants.LANG_CLASSES) + "
",
"" + langFile.getProperty(Constants.LANG_CLASSES)
+ "
");
+ classes = extParser.parse(classes);
break;
case "objectproperties":
propertyList = getTermList(html.item(i));
@@ -197,6 +204,7 @@ private void parse(String content, Properties langFile) {
properties = properties.replace("" + langFile.getProperty(Constants.LANG_OBJ_PROP) + "
",
"" + langFile.getProperty(Constants.LANG_OBJ_PROP)
+ "
");
+ properties = extParser.parse(properties);
break;
case "dataproperties":
dataPropList = (getTermList(html.item(i)));
@@ -204,6 +212,7 @@ private void parse(String content, Properties langFile) {
dataProp = dataProp.replace("" + langFile.getProperty(Constants.LANG_DATA_PROP) + "
",
""
+ langFile.getProperty(Constants.LANG_DATA_PROP) + "
");
+ dataProp = extParser.parse(dataProp);
break;
case "annotationproperties":
annotationPropList = (getTermList(html.item(i)));
@@ -212,6 +221,7 @@ private void parse(String content, Properties langFile) {
"" + langFile.getProperty(Constants.LANG_ANN_PROP) + "
",
""
+ langFile.getProperty(Constants.LANG_ANN_PROP) + "
");
+ annotationProp = extParser.parse(annotationProp);
break;
case "namedindividuals":
namedIndividualList = (getTermList(html.item(i)));
@@ -220,6 +230,7 @@ private void parse(String content, Properties langFile) {
"" + langFile.getProperty(Constants.LANG_NAMED_INDIV) + "
",
""
+ langFile.getProperty(Constants.LANG_NAMED_INDIV) + "
");
+ namedIndividuals = extParser.parse(namedIndividuals);
break;
/*missing: rules!*/
case "rules":
@@ -245,6 +256,7 @@ private void parse(String content, Properties langFile) {
swrlrules = (nodeToString(html.item(i)));
swrlrules = swrlrules.replace("SWRL rules
",
"SWRL rules
");
+ swrlrules = extParser.parse(swrlrules);
break;
}
}
diff --git a/src/main/resources/lode/cs.xml b/src/main/resources/lode/cs.xml
index 3f7fe75f..20dcc210 100644
--- a/src/main/resources/lode/cs.xml
+++ b/src/main/resources/lode/cs.xml
@@ -88,4 +88,5 @@
range includes
uses rule
redakční poznámka
+ externí vlastnosti
diff --git a/src/main/resources/lode/de.xml b/src/main/resources/lode/de.xml
index b0f735f1..e0abe4e0 100644
--- a/src/main/resources/lode/de.xml
+++ b/src/main/resources/lode/de.xml
@@ -89,4 +89,5 @@
Szenarien
verwendet die Regel
redaktionelle Anmerkung
+ externe Eigenschaft
\ No newline at end of file
diff --git a/src/main/resources/lode/en.xml b/src/main/resources/lode/en.xml
index 6574fc90..389601b0 100644
--- a/src/main/resources/lode/en.xml
+++ b/src/main/resources/lode/en.xml
@@ -90,4 +90,5 @@
editorial note
used by rule (in antecedent)
used by rule (in consequent)
+ external property
\ No newline at end of file
diff --git a/src/main/resources/lode/es.xml b/src/main/resources/lode/es.xml
index 3a4a35ec..0c0e9988 100644
--- a/src/main/resources/lode/es.xml
+++ b/src/main/resources/lode/es.xml
@@ -88,4 +88,5 @@
el rango incluye
utiliza regla
nota editorial
+ propiedad externa
\ No newline at end of file
diff --git a/src/main/resources/lode/extra.css b/src/main/resources/lode/extra.css
index 2cc73565..8d3fd795 100644
--- a/src/main/resources/lode/extra.css
+++ b/src/main/resources/lode/extra.css
@@ -46,6 +46,11 @@ h1 {
color:mediumpurple;
}
+.type-ep {
+ cursor:help;
+ color:mediumpurple;
+}
+
.type-ap {
cursor:help;
color:var(--type-ap);
diff --git a/src/main/resources/lode/extraction.xsl b/src/main/resources/lode/extraction.xsl
index 2dafcced..ce714212 100644
--- a/src/main/resources/lode/extraction.xsl
+++ b/src/main/resources/lode/extraction.xsl
@@ -1,6 +1,6 @@
+
+
@@ -1201,6 +1208,7 @@ http://www.oxygenxml.com/ns/doc/xsl ">
+
@@ -1210,6 +1218,12 @@ http://www.oxygenxml.com/ns/doc/xsl ">
+
+
+
+
+
+
"
@@ -1938,9 +1952,17 @@ http://www.oxygenxml.com/ns/doc/xsl ">
ap
-
+
ni
+
+
+ ep
+
@@ -2050,6 +2072,7 @@ http://www.oxygenxml.com/ns/doc/xsl ">
+
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * This class tests the correct parsing of external properties that cannot
+ * be transformed with the xslt by analyzing the html generated output.
+ */
+package widoco;
+
+import org.jsoup.nodes.Element;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import static org.junit.Assert.fail;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+
+
+/**
+ *
+ * @author vChavezB
+ */
+public class ExternalEntitiesTest {
+
+ /**
+ * Class to store fact properties
+ */
+ class Fact {
+ private String predicateIRI;
+ private String predicateType;
+ private String objectIRI;
+ private String objectType;
+
+ public Fact(String predicateIRI, String predicateType, String objectIRI, String objectType) {
+ this.predicateIRI = predicateIRI;
+ this.predicateType = predicateType;
+ this.objectIRI = objectIRI;
+ this.objectType = objectType;
+ }
+
+ public String getPredicateIRI() {
+ return predicateIRI;
+ }
+
+ public String getPredicateType() {
+ return predicateType;
+ }
+
+ public String getObjectIRI() {
+ return objectIRI;
+ }
+
+ public String getObjectType() {
+ return objectType;
+ }
+
+ }
+ static String docUri = "myDoc";
+ Configuration c;
+ static final private String ONT_NS = "http://www.external-entity.com/testCase/";
+ public ExternalEntitiesTest() {
+ c = new Configuration();
+ //set up where the files will be written. Otherwise, an error will be produced
+ c.setDocumentationURI(docUri);
+ c.setOverwriteAll(true);
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ deleteFiles(new File (docUri));
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ deleteFiles(c.getTmpFile());
+ }
+
+ private static void deleteFiles(File folder){
+ String[]entries = folder.list();
+ for(String s: entries){
+ File currentFile = new File(folder.getPath(),s);
+ if(currentFile.isDirectory()){
+ deleteFiles(currentFile);
+ }
+ else{
+ currentFile.delete();
+ }
+ }
+ folder.delete();
+ }
+
+ /**
+ * Get div element for a specific class with id
+ * @param doc
+ * @param className
+ * @param id
+ * @return
+ */
+ private static Element getDiv(Document doc, String className, String id){
+ Elements elements = doc.getElementsByAttributeValue("class", className);
+
+ // Iterate over the elements and filter by id
+ for (Element element : elements) {
+ if (id.equals(element.id())) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Find first superclass in html of a crossref document
+ * for a specific class
+ * @param doc the Jsoup document with the crossref section
+ * @param class_iri the class iri to look for
+ * @return Type of superclass. Expected values (type-c,type-ep), null if not found
+ */
+ private static String getSuperClassType(Document doc, String class_iri) {
+ Element ExtProjectElement = getDiv(doc, "entity", class_iri);
+ Element ddElement = ExtProjectElement.select("dt:containsOwn(has super-classes) + dd").first();
+ Element supElement = ddElement.select("sup").first();
+ // Check if the sup element was found
+ if (supElement != null) {
+ // Get the entity descriptor class
+ return supElement.attr("class");
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the descriptor type located in the sup tag for an individual
+ * @param doc
+ * @param individual_iri
+ * @return
+ */
+ private static String getIndividualClassType(Document doc, String individual_iri) {
+ Element classEntity = getDiv(doc, "entity", individual_iri);
+ Element ddElement = classEntity.select("dt:containsOwn(belongs to) + dd").first();
+ Element supElement = ddElement.select("sup").first();
+ if (supElement != null) {
+ // Get the entity descriptor class
+ return supElement.attr("class");
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the type of class for and individual
+ * @param doc
+ * @param individual_iri
+ * @return
+ */
+ private static String getIndividualClassIRI(Document doc, String individual_iri) {
+ Element individualEntity = getDiv(doc, "entity", individual_iri);
+ Element ddElement = individualEntity.select("dt:containsOwn(belongs to) + dd").first();
+ Element aElement = ddElement.select("a").first();
+ if (aElement != null) {
+ // Get the entity descriptor class
+ return aElement.attr("title");
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get a list of individual facts for a specific iri
+ * @param doc
+ * @param individual_iri
+ * @return
+ */
+ private ArrayList getIndividualFacts(Document doc, String individual_iri) {
+ Element individualEntity = getDiv(doc, "entity", individual_iri);
+ Element factsDtElement = individualEntity.select("dt:containsOwn(has facts)").first();
+ // Create a list to store Fact instances
+ ArrayList factList = new ArrayList<>();
+ if (factsDtElement != null) {
+ // Find following dd elements
+ Elements factDDElements = factsDtElement.nextElementSiblings().select("dd");
+
+ for (Element factElement : factDDElements) {
+ Elements aElements = factElement.select("a");
+ Elements supElements = factElement.select("sup");
+ Element predicateA = aElements.get(0);
+ Element predicateSup = supElements.get(0);
+ if (predicateA != null && predicateSup != null) {
+ String predicateIRI = predicateA.attr("title");
+ String predicateType = predicateSup.attr("class");
+ String objectIRI = "";
+ String objectType = "";
+ Element spanElement = factElement.selectFirst("span");
+ if (aElements.size()==2 && spanElement==null) {
+ Element nextAElement = factElement.select("a").get(1);
+ Element nextSupElement = factElement.select("sup").get(1);
+ if (nextAElement != null && nextSupElement != null) {
+ objectIRI = nextAElement.attr("title");
+ objectType = nextSupElement.attr("class");
+ }
+ } else {
+ // Literal
+ if (spanElement!=null) {
+ objectIRI = spanElement.attr("class");
+ objectType = spanElement.text();
+ }
+ }
+ Fact fact = new Fact(predicateIRI, predicateType, objectIRI, objectType);
+ factList.add(fact);
+ }
+ }
+ }
+ return factList;
+ }
+
+ /**
+ * Test an individual and its expected class iri and descriptor type
+ * @param doc
+ * @param iri
+ * @param expectedClassIRI
+ * @param expectedType
+ */
+ static void testIndividual(Document doc, String iri,String expectedClassIRI,String expectedType) {
+ String entityType = getIndividualClassType(doc,iri);
+ String classIRI = getIndividualClassIRI(doc,iri);
+ assert(entityType.equals(expectedType));
+ assert(classIRI.equals(expectedClassIRI));
+ }
+
+
+ /**
+ * Helper function to assert a fact
+ * @param fact
+ * @param expectedPredicateIRI
+ * @param expectedPredicateType
+ * @param expectedObjectIRI
+ * @param expectedObjType
+ */
+ static void testFact(Fact fact,String expectedPredicateIRI, String expectedPredicateType, String expectedObjectIRI, String expectedObjType) {
+ assert(fact.getPredicateIRI().equals(expectedPredicateIRI));
+ assert(fact.getPredicateType().equals(expectedPredicateType));
+ assert(fact.getObjectIRI().equals(expectedObjectIRI));
+ assert(fact.getObjectType().equals(expectedObjType));
+ }
+
+ /**
+ * Test that parsing external entity works
+ * Generate the html and look for the facts and
+ * entity descriptors generated with sup tags.
+ */
+ @org.junit.Test
+ public void testExternalEntityOntology() {
+ System.out.println("Testing Ontology: External Entity");
+
+ try{
+ String pathToOnto = "test" + File.separator + "external-entity.ttl";
+ c.setFromFile(true);
+ this.c.setOntologyPath(pathToOnto);
+ //read the model from file
+ WidocoUtils.loadModelToDocument(c);
+ CreateResources.generateDocumentation(c.getDocumentationURI(), c, c.getTmpFile());
+ File crossRefFile = new File(c.getDocumentationURI()+"/sections/crossref-en.html");
+ Document crossRefDoc = Jsoup.parse(crossRefFile, "UTF-8");
+ // Look for superclass of ExtProject
+ // i.e., http://xmlns.com/foaf/0.1/Project should be recognized as type-c
+ String extProjectSuperClassType = getSuperClassType(crossRefDoc,ONT_NS+"ExtProject");
+ assert(extProjectSuperClassType!=null);
+ assert(extProjectSuperClassType.equals("type-c"));
+ testIndividual(crossRefDoc,ONT_NS+"PersonA","http://www.w3.org/2000/10/swap/pim/contact#Person","type-c");
+ testIndividual(crossRefDoc,ONT_NS+"PersonB",ONT_NS+"LocalPerson","type-c");
+ testIndividual(crossRefDoc,ONT_NS+"Project1",ONT_NS+"ExtProject","type-c");
+ ArrayList personAFacts = getIndividualFacts(crossRefDoc, ONT_NS + "PersonA");
+ assert(personAFacts.size() == 1);
+ testFact(personAFacts.get(0),"http://my-external-ont.com/ext/Annotation","type-ap",
+ "literal","\"external annotation\"@en");
+ ArrayList personBFacts = getIndividualFacts(crossRefDoc, ONT_NS + "PersonB");
+ assert(personBFacts.size() == 2);
+ testFact(personBFacts.get(0),"http://xmlns.com/foaf/0.1/knows","type-op",
+ "http://www.external-entity.com/testCase/PersonA","type-ni");
+ testFact(personBFacts.get(1),"http://xmlns.com/foaf/0.1/age","type-dp",
+ "literal","\"30\"^^integer");
+
+ ArrayList project1Facts = getIndividualFacts(crossRefDoc, ONT_NS + "Project1");
+ assert(project1Facts.size() == 2);
+ testFact(project1Facts.get(0),"http://xmlns.com/foaf/0.1/fundedBy","type-op",
+ "http://www.external-entity.com/testCase/PersonA","type-ni");
+ testFact(project1Facts.get(1),"http://xmlns.com/foaf/0.1/title","type-dp",
+ "literal","\"The External Project\"@en");
+
+ }catch(Exception e){
+ fail("Error while running test "+e.getMessage());
+ }
+ }
+}
diff --git a/test/catalog-v001.xml b/test/catalog-v001.xml
new file mode 100644
index 00000000..83347859
--- /dev/null
+++ b/test/catalog-v001.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/external-entity.ttl b/test/external-entity.ttl
new file mode 100644
index 00000000..e86da35c
--- /dev/null
+++ b/test/external-entity.ttl
@@ -0,0 +1,64 @@
+@prefix : .
+@prefix ext: .
+@prefix owl: .
+@prefix rdf: .
+@prefix xml: .
+@prefix xsd: .
+@prefix rdfs: .
+@prefix vann: .
+@base .
+
+ rdf:type owl:Ontology ;
+ owl:imports ;
+ vann:preferredNamespaceUri "http://www.external-entity.com/testCase" .
+
+#################################################################
+# Annotation properties
+#################################################################
+
+### http://my-external-ont.com/ext/Annotation
+ext:Annotation rdf:type owl:AnnotationProperty .
+
+
+### http://purl.org/vocab/vann/preferredNamespaceUri
+vann:preferredNamespaceUri rdf:type owl:AnnotationProperty .
+
+
+#################################################################
+# Classes
+#################################################################
+
+### http://www.external-entity.com/testCase/ExtProject
+:ExtProject rdf:type owl:Class ;
+ rdfs:subClassOf .
+
+
+### http://www.external-entity.com/testCase/LocalPerson
+:LocalPerson rdf:type owl:Class .
+
+
+#################################################################
+# Individuals
+#################################################################
+
+### http://www.external-entity.com/testCase/PersonA
+:PersonA rdf:type owl:NamedIndividual ,
+ ;
+ ext:Annotation "external annotation"@en .
+
+
+### http://www.external-entity.com/testCase/PersonB
+:PersonB rdf:type owl:NamedIndividual ,
+ :LocalPerson ;
+ :PersonA ;
+ 30 .
+
+
+### http://www.external-entity.com/testCase/Project1
+:Project1 rdf:type owl:NamedIndividual ,
+ :ExtProject ;
+ :PersonA ;
+ "The External Project"@en .
+
+
+### Generated by the OWL API (version 4.5.26.2023-11-26T01:45:50Z) https://github.com/owlcs/owlapi