Skip to content

Commit

Permalink
Add closure parser for RDF
Browse files Browse the repository at this point in the history
The new parser will crawl the import graph and parse or partially
parse all ontologies allowing a full parse to be performed. Resolve
methods have been updated to remove retrieve imports that cannot be
resolved locally.
  • Loading branch information
phillord committed May 18, 2022
1 parent 5ff974e commit 4d44890
Show file tree
Hide file tree
Showing 11 changed files with 411 additions and 20 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ rio_api="0.6.1"
rio_xml="0.6.1"
pretty_rdf="0.1.2"
##pretty_rdf={path="./pretty_rdf"}
slurp = "1.0.1"
ureq={version="2.1.1", optional=true}

[features]
Expand Down
20 changes: 14 additions & 6 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,23 @@ pub fn path_type(path: &Path) -> Option<ResourceType> {

pub fn parse_path(path: &Path) -> Result<ParserOutput<Rc<str>, Rc<AnnotatedAxiom<Rc<str>>>>, CommandError> {
let file = File::open(&path).map_err(underlying)?;
let mut bufreader = BufReader::new(file);
let bufreader = BufReader::new(file);

Ok(match path_type(path) {
Some(ResourceType::OWX) => super::io::owx::reader::read(&mut bufreader)
.map_err(underlying)?
.into(),
Some(ResourceType::RDF) => super::io::rdf::reader::read(&mut bufreader)
Some(ResourceType::OWX) => {
let file = File::open(&path).map_err(underlying)?;
let mut bufreader = BufReader::new(file);
super::io::owx::reader::read(&mut bufreader)
.map_err(underlying)?
.into(),
.into()
},
Some(ResourceType::RDF) => {
let b = Build::new();
let iri = super::resolve::pathbuf_to_file_iri(&b,&path.to_path_buf());
super::io::rdf::closure_reader::read(&iri)
.map_err(underlying)?
.into()
},
None => {
return Err(underlying(ParserError::FormatNotSupported {
path: path.into(),
Expand Down
162 changes: 162 additions & 0 deletions src/io/rdf/closure_reader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use crate::model::Build;
use crate::model::ForIRI;
use crate::model::IRI;
use crate::model::Ontology;
use crate::io::rdf::reader::OntologyParser;
use crate::io::rdf::reader::ReadError;
use crate::io::RDFOntology;
use crate::io::IncompleteParse;
use crate::ontology::indexed::ForIndex;
use crate::resolve::resolve_iri;
use crate::io::rdf::reader::parser_with_build;

use std::collections::HashMap;
use std::path::PathBuf;

pub struct ClosureOntologyParser<'a, A:ForIRI, AA:ForIndex<A>> {
op: HashMap<IRI<A>, OntologyParser<'a, A, AA>>,
import_map: HashMap<IRI<A>, Vec<IRI<A>>>,
b: &'a Build<A>,
}

impl<'a, A:ForIRI, AA:ForIndex<A>> ClosureOntologyParser<'a, A, AA> {

pub fn new(b: &'a Build<A>) -> Self {
ClosureOntologyParser{b, import_map: HashMap::new(), op: HashMap::new().into()}
}

pub fn parse_path(pb: &PathBuf) {
//let file = File::open(&pb)?;brea
//let reader = io::BufReader::new(file);
todo!()
}

pub fn parse_iri(&mut self, iri: &IRI<A>, doc_iri: Option<&IRI<A>>) -> Vec<IRI<A>> {
let mut v = vec![];
self.parse_iri_1(iri, doc_iri, &mut v);

v
}

fn parse_iri_1(&mut self, iri: &IRI<A>, doc_iri: Option<&IRI<A>>, v:&mut Vec<IRI<A>>) {
let (new_doc_iri, s) = resolve_iri(iri, doc_iri);
v.push(iri.clone());
let mut p = parser_with_build(&mut s.as_bytes(), self.b);
let imports = p.parse_imports().unwrap();
p.parse_declarations();
self.import_map.insert(iri.clone(), imports.clone());

let o = p.mut_ontology_ref();
::std::mem::swap(o.mut_doc_iri(), &mut Some(new_doc_iri.clone()));
self.op.insert(iri.clone(), p);

for iri in imports {
// check we haven't already
self.parse_iri_1(&iri, doc_iri.or(Some(&new_doc_iri)), v);
}
}

// Finish the parse for the ontology at index `i`
pub fn finish_parse(&mut self, iri: &IRI<A>) {

let op_pointer: *mut HashMap<_, _> = &mut self.op;

let import_iris = self.import_map.get(iri).unwrap();
let import_closure:Vec<_> = import_iris.iter()
.map(|i| self.op.get(i).unwrap().ontology_ref()).collect();

dbg!(&iri);
dbg!(&import_closure);
// The import closure references ontologies in the op
// HashMap. We need to modify one of the ontologies in the map
// while retaining a reference to the others. Hence the unsafe.
unsafe{
(*op_pointer).get_mut(iri).unwrap().finish_parse(&import_closure);
}
}

fn parse_iri_if_needed(&self, iri: &IRI<A>) {
// Parse an IRI that has been imported unless it's already in Vec

//
}

// Return ontology in potentially incompletely parsed state
pub fn as_ontology_vec(self) -> Vec<RDFOntology<A, AA>> {
todo!()
}

// Return ontology in potentially incompletely parsed state
pub fn as_ontology_vec_and_incomplete(self) -> Vec<(RDFOntology<A, AA>, IncompleteParse<A>)> {
self.op.into_values().map(|op| op.as_ontology_and_incomplete().unwrap()).collect()
}
}


// Parse the ontology at IRI, resolving any knowledge from imports necessary
pub fn read<A:ForIRI, AA:ForIndex<A>>(iri: &IRI<A>) -> Result<(RDFOntology<A, AA>, IncompleteParse<A>), ReadError> {
// Do parse, then full parse of first, drop the rest
let b = Build::new();
let mut c = ClosureOntologyParser::new(&b);
c.parse_iri(iri, None);

let keys:Vec<_> = c.op.keys().map(|k|k.clone()).collect();
for i in keys.clone() {
c.finish_parse(&i);
}

let res = c.as_ontology_vec_and_incomplete();
Ok(res.into_iter().next().unwrap())
}


pub fn read_closure<A:ForIRI, AA:ForIndex<A>>(b: &Build<A>, iri: &IRI<A>)
-> Result<Vec<(RDFOntology<A, AA>, IncompleteParse<A>)>, ReadError> {
// Do parse, then full parse, then result the results
let mut c = ClosureOntologyParser::new(b);
c.parse_iri(iri, None);
let keys:Vec<_> = c.op.keys().map(|k|k.clone()).collect();
for i in keys.clone() {
c.finish_parse(&i);
}

Ok(c.as_ontology_vec_and_incomplete())
}


#[cfg(test)]
mod test {
use crate::ontology::set::SetOntology;
use crate::io::rdf::reader::RcRDFOntology;
use crate::io::rdf::closure_reader::*;
use crate::resolve::*;
use std::path::Path;

#[test]
fn test_read() {
let path_buf = Path::new("src/ont/owl-rdf/import-property.owl").to_path_buf();
let b = Build::new_rc();
let iri = pathbuf_to_file_iri(&b, &path_buf);

let (rdfo, ic):(RcRDFOntology, _) = read(&iri).unwrap();
assert!(ic.is_complete());
}


// import-property.owl should parse completely with full parse so
// is a good test.
#[test]
fn test_read_closure() {
let path_buf = Path::new("src/ont/owl-rdf/import-property.owl").to_path_buf();
let b = Build::new_rc();
let iri = pathbuf_to_file_iri(&b, &path_buf);

let v:Vec<(RcRDFOntology, _)> = read_closure(&b, &iri).unwrap();
let v:Vec<SetOntology<_>> = v.into_iter().map(|(rdfo, ic)| {
assert!(ic.is_complete());
rdfo.into()
}).collect();

assert_eq!(v.len(), 2);
}
}
1 change: 1 addition & 0 deletions src/io/rdf/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod closure_reader;
pub mod reader;
pub mod writer;
40 changes: 35 additions & 5 deletions src/io/rdf/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,27 @@ pub struct RDFOntology<A:ForIRI, AA:ForIndex<A>> (
>
);


pub type RcRDFOntology = RDFOntology<Rc<str>, Rc<AnnotatedAxiom<Rc<str>>>>;

impl<A:ForIRI, AA:ForIndex<A>> Ontology<A> for RDFOntology<A, AA> {
fn id(&self) -> &OntologyID<A> {
self.0.id()
}

fn mut_id(&mut self) -> &mut OntologyID<A> {
self.0.mut_id()
}

fn doc_iri(&self) -> &Option<IRI<A>> {
self.0.doc_iri()
}

fn mut_doc_iri(&mut self) -> &mut Option<IRI<A>> {
self.0.mut_doc_iri()
}
}

impl<A: ForIRI, AA: ForIndex<A>> From<RDFOntology<A, AA>> for SetOntology<A> {
fn from(so: RDFOntology<A, AA>) -> SetOntology<A> {
let id: OntologyID<_> = so.0.id().clone();
Expand Down Expand Up @@ -488,10 +509,12 @@ impl<'a, A: ForIRI, AA:ForIndex<A>> OntologyParser<'a, A, AA> {
}
}

fn resolve_imports(&mut self) {
fn resolve_imports(&mut self) -> Vec<IRI<A>> {
let mut v = vec![];
for t in std::mem::take(&mut self.simple) {
match t {
[Term::Iri(_), Term::OWL(VOWL::Imports), Term::Iri(imp)] => {
v.push(imp.clone());
self.merge(AnnotatedAxiom {
axiom: Import(imp).into(),
ann: BTreeSet::new(),
Expand All @@ -501,6 +524,7 @@ impl<'a, A: ForIRI, AA:ForIndex<A>> OntologyParser<'a, A, AA> {
}
}

v
// Section 3.1.2/table 4 of RDF Graphs
}

Expand Down Expand Up @@ -1605,7 +1629,7 @@ impl<'a, A: ForIRI, AA:ForIndex<A>> OntologyParser<'a, A, AA> {

/// Parse all imports and add to the Ontology.
/// Return an error is we are in the wrong state
pub fn parse_imports(&mut self) -> Result<(), ReadError> {
pub fn parse_imports(&mut self) -> Result<Vec<IRI<A>>, ReadError> {
match self.state {
OntologyParserState::New => {
let triple = std::mem::take(&mut self.triple);
Expand All @@ -1620,9 +1644,9 @@ impl<'a, A: ForIRI, AA:ForIndex<A>> OntologyParser<'a, A, AA> {

// Table 10
self.axiom_annotations();
self.resolve_imports();
let v = self.resolve_imports();
self.state = OntologyParserState::Imports;
Ok(())
Ok(v)
}
_ => todo!(),
}
Expand Down Expand Up @@ -1722,7 +1746,9 @@ impl<'a, A: ForIRI, AA:ForIndex<A>> OntologyParser<'a, A, AA> {

match self.state {
OntologyParserState::New => {
self.error = self.parse_imports();
// Ditch the vec that this might return as we don't
// need it!
self.error = self.parse_imports().and(Ok(()));
self.parse()
}
OntologyParserState::Imports => {
Expand All @@ -1741,6 +1767,10 @@ impl<'a, A: ForIRI, AA:ForIndex<A>> OntologyParser<'a, A, AA> {
&self.o
}

pub fn mut_ontology_ref(&mut self) -> &mut RDFOntology<A, AA> {
&mut self.o
}

/// Consume the parser and return an Ontology.
pub fn as_ontology(self) -> Result<RDFOntology<A, AA>, ReadError> {
self.error.and(Ok(self.o))
Expand Down
14 changes: 14 additions & 0 deletions src/ont/bubo/other-iri.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(ns other
(:use [tawny.owl]))

(defontology other
:iri "http://www.example.com/other-iri"
:viri "http://www.example.com/other-iri-viri"
:noname true)

(defclass C)


(alias 'cc 'clojure.core)
(load-file "save.clj")
(save-all)
35 changes: 35 additions & 0 deletions src/ont/owl-rdf/other-iri.owl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0"?>
<rdf:RDF xmlns="http://www.example.com/other-iri#"
xml:base="http://www.example.com/other-iri"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:other="http://www.example.com/other-iri#">
<owl:Ontology rdf:about="http://www.example.com/other-iri">
<owl:versionIRI rdf:resource="http://www.example.com/other-iri-viri"/>
</owl:Ontology>



<!--
///////////////////////////////////////////////////////////////////////////////////////
//
// Classes
//
///////////////////////////////////////////////////////////////////////////////////////
-->




<!-- http://www.example.com/other-iri#C -->

<owl:Class rdf:about="http://www.example.com/other-iri#C"/>
</rdf:RDF>



<!-- Generated by the OWL API (version 4.5.19) https://github.com/owlcs/owlapi -->

21 changes: 21 additions & 0 deletions src/ont/owl-ttl/other-iri.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@prefix : <http://www.example.com/other-iri#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix other: <http://www.example.com/other-iri#> .
@base <http://www.example.com/other-iri> .

<http://www.example.com/other-iri> rdf:type owl:Ontology ;
owl:versionIRI <http://www.example.com/other-iri-viri> .

#################################################################
# Classes
#################################################################

### http://www.example.com/other-iri#C
other:C rdf:type owl:Class .


### Generated by the OWL API (version 4.5.19) https://github.com/owlcs/owlapi
24 changes: 24 additions & 0 deletions src/ont/owl-xml/other-iri.owx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0"?>
<Ontology xmlns="http://www.w3.org/2002/07/owl#"
xml:base="http://www.example.com/other-iri"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
ontologyIRI="http://www.example.com/other-iri"
versionIRI="http://www.example.com/other-iri-viri">
<Prefix name="owl" IRI="http://www.w3.org/2002/07/owl#"/>
<Prefix name="rdf" IRI="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>
<Prefix name="xml" IRI="http://www.w3.org/XML/1998/namespace"/>
<Prefix name="xsd" IRI="http://www.w3.org/2001/XMLSchema#"/>
<Prefix name="rdfs" IRI="http://www.w3.org/2000/01/rdf-schema#"/>
<Prefix name="other" IRI="http://www.example.com/other-iri#"/>
<Declaration>
<Class IRI="#C"/>
</Declaration>
</Ontology>



<!-- Generated by the OWL API (version 4.5.19) https://github.com/owlcs/owlapi -->

Loading

0 comments on commit 4d44890

Please sign in to comment.