diff --git a/.github/workflows/create-artifacts.yml b/.github/workflows/create-artifacts.yml index a7265a5..a517125 100644 --- a/.github/workflows/create-artifacts.yml +++ b/.github/workflows/create-artifacts.yml @@ -1,64 +1,71 @@ name: Create artifacts on: - [push] + push: + +permissions: + contents: read env: CARGO_TERM_COLOR: always jobs: - build-binaries: + build: strategy: + fail-fast: false matrix: - target: [ - { runner: "macos-11", arch: "x86_64-apple-darwin" }, - { runner: "macos-11", arch: "aarch64-apple-darwin" }, - { runner: "windows-2022", arch: "x86_64-pc-windows-msvc" }, - { runner: "ubuntu-20.04", arch: "x86_64-unknown-linux-gnu" }, - { runner: "ubuntu-20.04", arch: "x86_64-unknown-linux-musl" }, - ] - runs-on: ${{ matrix.target.runner }} + include: + - os: macos-15-intel + target: x86_64-apple-darwin + ext: "" + - os: macos-15 + target: aarch64-apple-darwin + ext: "" + - os: windows-2022 + target: x86_64-pc-windows-msvc + ext: ".exe" + - os: ubuntu-24.04 + target: x86_64-unknown-linux-gnu + ext: "" + - os: ubuntu-24.04 + target: x86_64-unknown-linux-musl + ext: "" + use_cross: true + + runs-on: ${{ matrix.os }} + steps: - - name: Check out repository code - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - - name: Install musl-tools (MUSL) - if: ${{ matrix.target.runner == 'ubuntu-20.04' && matrix.target.arch == 'x86_64-unknown-linux-musl' }} - run: | - sudo apt-get update - sudo apt-get install musl-tools + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - uses: Swatinem/rust-cache@v2 - - name: Install latest rust toolchain - uses: actions-rs/toolchain@v1 + - name: Install cross (MUSL only) + if: ${{ matrix.use_cross }} + uses: taiki-e/install-action@v2 with: - toolchain: stable - target: ${{ matrix.target.arch }} - default: true - override: true + tool: cross - - name: Build binary using cross (Linux) - if: ${{ matrix.target.runner == 'ubuntu-20.04' }} + - name: Build + package + shell: bash run: | - cargo install cross --git https://github.com/cross-rs/cross - sudo systemctl start docker - cross build --release --target ${{ matrix.target.arch }} - cp target/${{ matrix.target.arch }}/release/ldtab ldtab-${{ matrix.target.arch }} + set -euxo pipefail - - name: Build binary using cargo (MacOS) - if: ${{ matrix.target.runner == 'macos-11' }} - run: | - cargo build --release --target ${{ matrix.target.arch }} - cp target/${{ matrix.target.arch }}/release/ldtab ldtab-${{ matrix.target.arch }} + if [[ "${{ matrix.use_cross }}" == "true" ]]; then + cross build --release --target "${{ matrix.target }}" + else + cargo build --release --target "${{ matrix.target }}" + fi - - name: Build binary using cargo (Windows) - if: ${{ matrix.target.runner == 'windows-2022' }} - run: | - cargo build --release --target ${{ matrix.target.arch }} - cp target\${{ matrix.target.arch }}\release\ldtab.exe ldtab-${{ matrix.target.arch }}.exe + mkdir -p dist + cp "target/${{ matrix.target }}/release/ldtab${{ matrix.ext }}" \ + "dist/ldtab-${{ matrix.target }}${{ matrix.ext }}" - - name: Upload binary artifacts - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v6 with: - name: ${{ matrix.target.arch }}-bin - path: ldtab-${{ matrix.target.arch }} + name: ${{ matrix.target }}-bin + path: dist/ldtab-${{ matrix.target }}${{ matrix.ext }} if-no-files-found: error diff --git a/Cargo.toml b/Cargo.toml index acb77f8..34c6edf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,17 @@ wiring_rs = { path = "../wiring.rs" } serde = { version = "1.0", features = ["derive"] } regex = "1.6.0" serde_json = "1.0" -horned-owl = "0.12.0" +sha2 = "0.10" +horned-owl = "1.0.0" +horned-bin = "1.0.0" im = "15.1.0" itertools = "0.10.3" +rayon = "1.7" rio_api = "0.7.1" rio_xml = "0.7.3" +clap = { version = "4.5.11", features = ["cargo", "derive"] } +sqlx = { version = "0.7", features = ["sqlite", "runtime-tokio-native-tls"] } +tokio = { version = "1", features = ["full"] } +anyhow = "1.0.86" +csv = "1.1" +once_cell = "1.17.1" diff --git a/src/export/export.rs b/src/export/export.rs new file mode 100644 index 0000000..ffae20c --- /dev/null +++ b/src/export/export.rs @@ -0,0 +1,75 @@ +use anyhow::{Context, Result}; +use clap::ArgMatches; +use sqlx::{sqlite::SqlitePoolOptions, Row}; +use std::fs::OpenOptions; +use std::io::Write; + +fn escape_whitespace(input: &str) -> String { + input + .chars() + .map(|c| match c { + '\n' => "\\n".to_string(), + '\t' => "\\t".to_string(), + //'\r' => "\\r".to_string(), + //'\x08' => "\\b".to_string(), + //'\x0C' => "\\f".to_string(), + //'\x0B' => "\\v".to_string(), + //' ' => "\\s".to_string(), // Optional: Escape space as '\\s' + _ => c.to_string(), + }) + .collect() +} + +pub async fn export(sub_matches: &ArgMatches) -> Result<()> { + let database = sub_matches.get_one::("database"); + let output = sub_matches.get_one::("output"); + + //these unwraps are safe + let database_path = database.unwrap(); + let output_path = output.unwrap(); + + let pool = SqlitePoolOptions::new() + .max_connections(5) + .connect(database_path) + .await + .context("Failed to connect to the database")?; + + //load data + let rows = sqlx::query( + r#" + SELECT * FROM statement + "#, + ) + .fetch_all(&pool) + .await + .context("Failed to fetch rows from the table")?; + + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open(output_path)?; + + let header = "assertion\tretraction\tgraph\tsubject\tpredicate\tobject\tdatatype\tannotation"; + file.write_all(header.as_bytes())?; + file.write_all(b"\n")?; + + for row in rows { + let assertion: i32 = row.get("assertion"); + let retraction: i32 = row.get("retraction"); + let graph: String = row.get("graph"); + let subject: String = row.get("subject"); + let predicate: String = row.get("predicate"); + let object: String = escape_whitespace(row.get("object")); + let datatype: String = row.get("datatype"); + let annotation: String = row.get("annotation"); + + let line = format!( + "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}", + assertion, retraction, graph, subject, predicate, object, datatype, annotation + ); + + file.write_all(line.as_bytes())?; + file.write_all(b"\n")?; + } + Ok(()) +} diff --git a/src/export/mod.rs b/src/export/mod.rs new file mode 100644 index 0000000..8a42758 --- /dev/null +++ b/src/export/mod.rs @@ -0,0 +1 @@ +pub mod export; diff --git a/src/import/curify.rs b/src/import/curify.rs new file mode 100644 index 0000000..16ff8ff --- /dev/null +++ b/src/import/curify.rs @@ -0,0 +1,154 @@ +use once_cell::sync::Lazy; +use regex::Regex; +use serde_json::Value; +use sha2::{Digest, Sha256}; +use std::collections::HashMap; + +use super::triple::LdTabTriple; + +pub(crate) static DATATYPE_LITERAL_REGEX: Lazy = + Lazy::new(|| Regex::new(r#"^"(?s)(.*)"\^\^(.*)$"#).unwrap()); + +pub(crate) fn curify_triples( + triples: &[LdTabTriple], + prefix_map: &HashMap, +) -> Vec { + triples + .iter() + .map(|t| { + LdTabTriple { + assertion: t.assertion, + retraction: t.retraction, + graph: t.graph.clone(), + subject: curify_value(&t.subject, prefix_map), + predicate: curify_value(&t.predicate, prefix_map), + object: curify_value(&t.object, prefix_map), + datatype: curify_value(&t.datatype, prefix_map), + annotation: curify_value(&t.annotation, prefix_map), + } + }) + .collect() +} + +fn curify_value(ldtab: &Value, iri2prefix: &HashMap) -> Value { + match ldtab { + Value::Array(vec) => { + let new_vec: Vec = vec + .iter() + .map(|item| curify_value(item, iri2prefix)) + .collect(); + Value::Array(new_vec) + } + Value::Object(map) => { + let mut new_map = serde_json::Map::new(); + for (key, value) in map.iter() { + let curified_key = replace_substrings(key, iri2prefix); + new_map.insert(curified_key, curify_value(value, iri2prefix)); + } + Value::Object(new_map) + } + Value::String(s) => Value::String(replace_substrings(s, iri2prefix)), + _ => ldtab.clone(), + } +} + +pub(crate) fn generate_blank_node_id(value: &Value, prefix2iri: &HashMap) -> String { + let expanded = uncurify_value(value, prefix2iri); + let sorted = wiring_rs::ofn_2_ldtab::util::sort_value(&expanded); + let json_string = sorted.to_string(); + + let mut hasher = Sha256::new(); + hasher.update(json_string.as_bytes()); + let hash = hasher.finalize(); + + format!("", hash) +} + +pub(crate) fn is_full_iri(s: &str) -> bool { + s.starts_with('<') && s.ends_with('>') +} + +pub(crate) fn invert_prefix_map(iri2prefix: &HashMap) -> HashMap { + iri2prefix + .iter() + .map(|(iri_base, prefix)| (prefix.clone(), iri_base.clone())) + .collect() +} + +fn try_curify_iri(iri: &str, iri2prefix: &HashMap) -> Option { + let trimmed = &iri[1..iri.len() - 1]; // remove angle brackets + for (key, value) in iri2prefix { + if trimmed.starts_with(key) { + return Some(format!("{}:{}", value, &trimmed[key.len()..])); + } + } + None +} + +fn replace_substrings(input: &str, iri2prefix: &HashMap) -> String { + if is_full_iri(input) { + try_curify_iri(input, iri2prefix).unwrap_or_else(|| input.to_string()) + } else if let Some(x) = DATATYPE_LITERAL_REGEX.captures(input) { + let literal = &x[1]; + let datatype_iri = &x[2]; + + match try_curify_iri(datatype_iri, iri2prefix) { + Some(curified) => format!("\"{}\"^^{}", literal, curified), + None => input.to_string(), + } + } else { + input.to_string() + } +} + +fn uncurify_value(ldtab: &Value, prefix2iri: &HashMap) -> Value { + match ldtab { + Value::Array(vec) => { + let new_vec: Vec = vec + .iter() + .map(|item| uncurify_value(item, prefix2iri)) + .collect(); + Value::Array(new_vec) + } + Value::Object(map) => { + let mut new_map = serde_json::Map::new(); + for (key, value) in map.iter() { + let expanded_key = expand_curies(key, prefix2iri); + new_map.insert(expanded_key, uncurify_value(value, prefix2iri)); + } + Value::Object(new_map) + } + Value::String(s) => Value::String(expand_curies(s, prefix2iri)), + _ => ldtab.clone(), + } +} + +fn expand_curies(input: &str, prefix2iri: &HashMap) -> String { + if let Some(expanded) = expand_curie_to_iri(input, prefix2iri) { + return expanded; + } + + if let Some(caps) = DATATYPE_LITERAL_REGEX.captures(input) { + let literal = &caps[1]; + let dtype = &caps[2]; + + if let Some(expanded_dtype) = expand_curie_to_iri(dtype, prefix2iri) { + return format!("\"{}\"^^{}", literal, expanded_dtype); + } + } + + input.to_string() +} + +fn expand_curie_to_iri(curie: &str, prefix2iri: &HashMap) -> Option { + if is_full_iri(curie) { + return None; + } + + let mut parts = curie.splitn(2, ':'); + let prefix = parts.next()?; + let local = parts.next()?; + + let iri_base = prefix2iri.get(prefix)?; + Some(format!("<{}{}>", iri_base, local)) +} diff --git a/src/import/db.rs b/src/import/db.rs new file mode 100644 index 0000000..b51b406 --- /dev/null +++ b/src/import/db.rs @@ -0,0 +1,62 @@ +use anyhow::{Context, Result}; +use sqlx::{QueryBuilder, Row, SqlitePool}; +use std::collections::HashMap; + +use super::triple::{value_to_db_string, LdTabTriple}; + +const SQLITE_MAX_VARIABLE_NUMBER: usize = 999; +const NUM_COLUMNS: usize = 8; + +pub(crate) async fn load_prefix_map(pool: &SqlitePool) -> Result> { + let rows = sqlx::query( + r#" + SELECT * FROM prefix + "#, + ) + .fetch_all(pool) + .await + .context("Failed to fetch rows from the table")?; + + let mut map = HashMap::new(); + for row in rows { + let base: String = row.get("base"); + let prefix: String = row.get("prefix"); + map.insert(base, prefix); + } + Ok(map) +} + +pub(crate) async fn insert_triples_to_db(triples: &[LdTabTriple], pool: &SqlitePool) -> Result<()> { + let mut start = 0; + while start < triples.len() { + let end = std::cmp::min( + start + SQLITE_MAX_VARIABLE_NUMBER / NUM_COLUMNS, + triples.len(), + ); + let chunk = &triples[start..end]; + + let mut query_builder = QueryBuilder::new( + "INSERT INTO statement (assertion, retraction, graph, subject, predicate, object, datatype, annotation) ", + ); + + query_builder.push_values(chunk, |mut b, triple| { + b.push_bind(triple.assertion) + .push_bind(triple.retraction) + .push_bind(&triple.graph) + .push_bind(value_to_db_string(&triple.subject)) + .push_bind(value_to_db_string(&triple.predicate)) + .push_bind(value_to_db_string(&triple.object)) + .push_bind(value_to_db_string(&triple.datatype)) + .push_bind(value_to_db_string(&triple.annotation)); + }); + + query_builder + .build() + .execute(pool) + .await + .context("Failed to insert ldtab_triples into the database")?; + + start = end; + } + Ok(()) +} \ No newline at end of file diff --git a/src/import/import.rs b/src/import/import.rs index 589e70a..67ee1fc 100644 --- a/src/import/import.rs +++ b/src/import/import.rs @@ -1,141 +1,414 @@ -use serde_json::{Value}; -use serde_json::json; -use horned_owl::model::{IRI, AnnotatedAxiom, Ontology, Axiom, Import, OntologyAnnotation, RcStr}; +use anyhow::{Context, Result}; +use clap::ArgMatches; +use horned_owl::io::owx::reader::*; +use horned_owl::model::*; use horned_owl::ontology::set::SetOntology; +use rayon::prelude::*; +use serde_json::{json, Map, Value}; +use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; +use std::collections::{HashMap, HashSet}; +use std::fs::File; +use std::io::BufReader; +use std::time::Instant; + +use crate::owl_2_ofn; + +use super::curify::{curify_triples, generate_blank_node_id, invert_prefix_map}; +use super::db::{insert_triples_to_db, load_prefix_map}; +use super::triple::{ + extract_value_and_datatype, is_ldtab_blanknode, ldtab_2_triple, + LdTabTriple, DATATYPE_IRI, UNKNOWN_VALUE, +}; + +const RDF_TYPE: &str = ""; +const OWL_VERSION_IRI: &str = ""; +const OWL_ONTOLOGY: &str = ""; +const OWL_DISJOINT_WITH: &str = ""; +const OWL_EQUIVALENT_CLASS: &str = ""; +const OWL_UNION_OF: &str = ""; +const RDFS_SUBCLASS_OF: &str = ""; +const SWRL_VARIABLE: &str = ""; + +pub async fn import(sub_matches: &ArgMatches) -> Result<()> { + let database = sub_matches.get_one::("database"); + let ontology = sub_matches.get_one::("ontology"); + + // This check should be redundant, as clap requires both arguments + if database.is_none() || ontology.is_none() { + anyhow::bail!("You must provide both a database and an ontology."); + } + //these unwraps are safe + let database_path = database.unwrap(); + let ontology_path = ontology.unwrap(); -use crate::owl_2_ofn::transducer as owl2ofn; -use crate::ofn_2_owl::transducer as ofn2owl; -use crate::owl_2_ofn::annotation_transducer as annotation_transducer; -use std::collections::HashSet; -use horned_owl::command::parse_path; -use std::path::Path; - -extern crate wiring_rs; - - -//Ideally, this would return a stream -pub fn import(path : &str) -> HashSet { + let file = File::open(ontology_path)?; + let reader = BufReader::new(file); + let build = Build::::new(); - match parse_path(Path::new(path)) { - Ok(parsed) => { - let ont = &parsed.decompose().0; + match read_with_build(reader, &build) { + Ok((ontology, _prefix_mapping)) => { + let pool = SqlitePoolOptions::new() + .max_connections(5) + .connect(database_path) + .await + .context("Failed to connect to the database")?; - process_ontology(ont) - + import_ontology(&ontology, &pool).await?; } - Err(error) => { panic!() } - } + Err(e) => { + anyhow::bail!("Failed to read ontology: {:?}", e); + } + } + Ok(()) } -fn process_ontology(ontology : &SetOntology) -> HashSet { +async fn import_ontology(ontology: &SetOntology, pool: &SqlitePool) -> Result<()> { + let iri_value = extract_ontology_iri(ontology)?; + let prefix_map = load_prefix_map(pool).await?; + let prefix2iri = invert_prefix_map(&prefix_map); - let mut res = HashSet::new(); + let count = ontology.iter().count(); + println!("Number of axioms: {}", count); - let id = ontology.id(); - let iri = id.clone().iri.unwrap(); - let iri_value = Value::String(String::from(iri.get(0..).unwrap())); + let (imports, ontology_annotations, dl_safe_rules, ontology_id, normal) = + split_ontology_components(ontology); - for ann_axiom in ontology.iter() { + let start = Instant::now(); - let ofn = owl2ofn::translate(ann_axiom); - let ofn = - match ofn[0].as_str() { - Some("Import") => Value::Array(vec![ofn[0].clone(), iri_value.clone(), ofn[1].clone()]), - Some("OntologyAnnotation") => Value::Array(vec![ofn[0].clone(), iri_value.clone(), ofn[1].clone()]) , - _ => ofn.clone() - }; + // Process axiom types into triples + let mut ldtab_triples = process_normal_axioms(&normal); + ldtab_triples.extend(process_ontology_id(&ontology_id)?); + ldtab_triples.extend(process_imports(&imports, &iri_value)?); + ldtab_triples.extend(process_ontology_annotations(&ontology_annotations, &iri_value)?); + ldtab_triples.extend(process_swrl_rules(&dl_safe_rules, &prefix2iri)?); - let ldtab = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); - res.insert(ldtab.to_string()); - } - res -} + // Handle blank nodes + let ldtab_triples = process_blank_nodes(&ldtab_triples, &prefix2iri); + + // curify all triples + let ldtab_triples = curify_triples(&ldtab_triples, &prefix_map); -pub fn ontology_2_ldtab(ontology : &SetOntology) { - //TODO a SetOntology has the field "doc_iri" - however this doesn't seem to be always set? - let id = ontology.id(); - let iri = id.clone().iri.unwrap(); //TODO can I assume that this exists? + let duration = start.elapsed(); + println!("OWL2LDTab took: {:?}", duration); - for ann_axiom in ontology.iter() { + // Insert into database + let time = Instant::now(); + insert_triples_to_db(&ldtab_triples, pool).await?; + let duration = time.elapsed(); + println!("Sqlite took: {:?}", duration); - let axiom = &ann_axiom.axiom; - let annotations = &ann_axiom.ann; + Ok(()) +} + +fn extract_ontology_iri(ontology: &SetOntology) -> Result { + let id = ontology.i(); + let ont_id = id + .clone() + .the_ontology_id() + .context("Ontology has no OntologyID component")?; + let iri = ont_id + .iri + .context("Ontology has no IRI")?; + let iri_str = iri + .get(0..) + .context("Failed to extract IRI string")?; + Ok(Value::String(format!("<{}>", iri_str))) +} - if !annotations.is_empty() { +fn split_ontology_components<'a>( + ontology: &'a SetOntology, +) -> ( + Vec<&'a AnnotatedComponent>, + Vec<&'a AnnotatedComponent>, + Vec<&'a AnnotatedComponent>, + Vec<&'a AnnotatedComponent>, + Vec<&'a AnnotatedComponent>, +) { + let mut imports = Vec::new(); + let mut ontology_annotations = Vec::new(); + let mut dl_safe_rules = Vec::new(); + let mut ontology_id = Vec::new(); + let mut normal = Vec::new(); + + ontology.iter().for_each(|ann_axiom| { + let component = &ann_axiom.component; + match component { + Component::Import(_) => imports.push(ann_axiom), + Component::OntologyAnnotation(_) => ontology_annotations.push(ann_axiom), + Component::Rule(_) => dl_safe_rules.push(ann_axiom), + Component::OntologyID(_) => ontology_id.push(ann_axiom), + _ => normal.push(ann_axiom), + } + }); - println!("0. Horned OWL: {:?}", ann_axiom); + (imports, ontology_annotations, dl_safe_rules, ontology_id, normal) +} - //translate horned OWL to LDTab Thick Triple - let inspect = match axiom { +fn process_normal_axioms( + normal: &[&AnnotatedComponent], +) -> Vec { + normal + .par_iter() + .filter_map(|ann_axiom| match owl_2_ldtab(ann_axiom) { + Ok(t) => Some(t), + Err(e) => { + eprintln!("Skipping axiom: {e:#}"); + None + } + }) + .collect() +} - //two special cases: - Axiom::Import(x) => translate_ontology_import(&x, &iri), - Axiom::OntologyAnnotation(x) => translate_ontology_annotation(&x, &iri), +fn process_ontology_id(ontology_id: &[&AnnotatedComponent]) -> Result> { + let mut triples = Vec::new(); - _ => owl_2_thick(&ann_axiom), - }; + for ann_axiom in ontology_id { + let ofn = owl_2_ofn::transducer::translate(ann_axiom); + let ldtab = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); + if ldtab["predicate"] == OWL_VERSION_IRI { + let mut t = ldtab_2_triple(&ldtab)?; + t.predicate = json!(RDF_TYPE); + t.object = json!(OWL_ONTOLOGY); + triples.push(t); + } + if ldtab["object"] != UNKNOWN_VALUE { + triples.push(ldtab_2_triple(&ldtab)?); } } + Ok(triples) } -//the following translations are two special cases that cannot be -//translated with the standard translation -//(because some related information is split in Horned OWL's data model) -pub fn translate_ontology_annotation(axiom : &OntologyAnnotation, ontology_iri : &IRI) -> Value { - //An ontolog annotation could be encoded as a OFN S-expression - //via - //["ThickTriple", s, p, o] - //or - //["Ontology", ["ThickTriple", s, p, o]]] - // - let annotation = axiom.0.clone(); - - let property = annotation.ap; - let value = annotation.av; - let value_ofn = annotation_transducer::translate_annotation_value(&value); - let value_ldtab = wiring_rs::ofn_2_ldtab::annotation_translation::translate_value(&value_ofn); - let value_datatype = wiring_rs::ofn_2_ldtab::util::translate_datatype(&value_ofn); - - //TODO: recrusive annotations (currently not supported in horned OWL) - //TODO: sort this - json!({"assertion":"1", - "retraction":"0", - "graph":"graph", - "subject":ontology_iri.get(0..), - "predicate":property.0.get(0..), - "object":value_ldtab, - "datatype":value_datatype, - "annotation":"Null" //TODO - }) +fn process_imports( + imports: &[&AnnotatedComponent], + iri_value: &Value, +) -> Result> { + imports + .iter() + .map(|ann_axiom| { + let ofn = owl_2_ofn::transducer::translate(ann_axiom); + let ofn = Value::Array(vec![ofn[0].clone(), iri_value.clone(), ofn[1].clone()]); + let ldtab = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); + ldtab_2_triple(&ldtab) + }) + .collect() } -pub fn translate_ontology_import(axiom : &Import, ontology_iri : &IRI) -> Value { - - //TODO: sort this - json!({"assertion":"1", - "retraction":"0", - "graph":"graph", - "subject":ontology_iri.get(0..), - "predicate":"owl:imports", - "object":axiom.0.get(0..), - "datatype":"_IRI", - "annotation":"Null" //TODO - }) +fn process_ontology_annotations( + ontology_annotations: &[&AnnotatedComponent], + iri_value: &Value, +) -> Result> { + ontology_annotations + .iter() + .map(|ann_axiom| { + let ofn = owl_2_ofn::transducer::translate(ann_axiom); + let ofn = Value::Array(vec![ofn[0].clone(), iri_value.clone(), ofn[1].clone()]); + let ldtab = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); + ldtab_2_triple(&ldtab) + }) + .collect() } -//translate a Horned OWL axiom to a LDTab ThickTriple -pub fn owl_2_thick(annotated_axiom : &AnnotatedAxiom) -> Value { +fn process_swrl_rules( + dl_safe_rules: &[&AnnotatedComponent], + prefix2iri: &HashMap, +) -> Result> { + let mut triples = Vec::new(); + + // Collect all variables from rules + let variables: HashSet> = dl_safe_rules + .iter() + .map(|ann_axiom| match &ann_axiom.component { + Component::Rule(r) => Ok(get_rule_variables(r)), + other => anyhow::bail!("Expected a Rule component, but found: {:?}", other), + }) + .collect::>>()? + .into_iter() + .flatten() + .collect(); + + // Create type declarations for variables + for var in &variables { + let var_iri = format!("<{}>", var.0); + triples.push(LdTabTriple::new(var_iri, RDF_TYPE, SWRL_VARIABLE, DATATYPE_IRI)); + } - //horned OWL to OFN S-expression - let ofn = owl2ofn::translate(annotated_axiom); - println!("1 OFN: {}", ofn); + // Process each rule + for ann_axiom in dl_safe_rules { + let ofn = owl_2_ofn::transducer::translate(ann_axiom); + let ldtab = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); + + // Build blank node content + let mut m = Map::new(); + let triples_array = ldtab + .as_array() + .context("Expected SWRL rule to produce a JSON array of triples")?; + + for triple in triples_array { + let predicate = triple + .get("predicate") + .and_then(|v| v.as_str()) + .context("SWRL triple missing 'predicate' string")?; + let object = triple + .get("object") + .context("SWRL triple missing 'object'")?; + let datatype = triple + .get("datatype") + .and_then(|v| v.as_str()) + .context("SWRL triple missing 'datatype' string")?; + let ooo = json!([{"datatype": datatype, "object": object}]); + m.insert(predicate.to_string(), ooo); + } + + let blank = Value::Object(m); + let blank_node = generate_blank_node_id(&blank, prefix2iri); + + for triple in triples_array { + let t = LdTabTriple::new( + blank_node.clone(), + triple.get("predicate").cloned().unwrap_or(Value::Null), + triple.get("object").cloned().unwrap_or(Value::Null), + triple.get("datatype").cloned().unwrap_or(Value::Null), + ) + .annotation(triple.get("annotation").cloned().unwrap_or(Value::Null)); + triples.push(t); + } + } - //OFN S-expression to LDTab ThickTriple - let thick_triple = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); + Ok(triples) +} + +/// Processes blank nodes in triples, expanding JSON objects as subjects. +fn process_blank_nodes( + ldtab_triples: &[LdTabTriple], + prefix2iri: &HashMap, +) -> Vec { + let mut new_ldtab_triples: Vec = Vec::new(); + + for t in ldtab_triples { + let s = &t.subject; + let o = &t.object; + + if is_ldtab_blanknode(s) { + if let Value::Object(map) = o { + for (key, value) in map.iter() { + let (value_v, datatype) = extract_value_and_datatype(value, &t.datatype); + new_ldtab_triples.push( + LdTabTriple::new(t.subject.clone(), key.clone(), value_v, datatype) + ); + } + } else { + new_ldtab_triples.push(t.clone()); + } + } else if s.is_object() { + if let Value::Object(map) = s { + let mut m = map.clone(); + let ooo = json!([{"datatype": t.datatype, "object": o.clone()}]); + + if let Some(p_str) = t.predicate.as_str() { + match p_str { + OWL_DISJOINT_WITH => { m.insert(OWL_DISJOINT_WITH.to_string(), ooo); } + RDFS_SUBCLASS_OF => { m.insert(RDFS_SUBCLASS_OF.to_string(), ooo); } + OWL_EQUIVALENT_CLASS => { m.insert(OWL_EQUIVALENT_CLASS.to_string(), ooo); } + OWL_UNION_OF => { m.insert(OWL_UNION_OF.to_string(), ooo); } + _ => {} + } + } + + let blank = Value::Object(m); + let blank_node = generate_blank_node_id(&blank, prefix2iri); + + for (key, value) in map.iter() { + let (value_v, datatype) = extract_value_and_datatype(value, &t.datatype); + new_ldtab_triples.push( + LdTabTriple::new(blank_node.clone(), key.clone(), value_v, datatype) + ); + } + + new_ldtab_triples.push( + LdTabTriple::new( + blank_node, + t.predicate.clone(), + t.object.clone(), + t.datatype.clone(), + ) + .annotation(t.annotation.clone()) + ); + } + } else { + new_ldtab_triples.push(t.clone()); + } + } + + new_ldtab_triples +} + +fn owl_2_ldtab( + ann_axiom: &AnnotatedComponent, +) -> Result { + let ofn = owl_2_ofn::transducer::translate(ann_axiom); + let ldtab = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); + ldtab_2_triple(&ldtab) + .context("Failed to convert thick triple to LdTabTriple") +} + +pub fn get_rule_variables(rule: &Rule) -> HashSet> +where + A: ForIRI, +{ + let mut vars = HashSet::new(); + + for atom in rule.head.iter().chain(rule.body.iter()) { + match *atom { + Atom::BuiltInAtom { ref args, .. } => { + for arg in args { + if let DArgument::Variable(ref var) = arg { + vars.insert(var.clone()); + } + } + } + Atom::ClassAtom { ref arg, .. } => { + if let IArgument::Variable(ref var) = arg { + vars.insert(var.clone()); + } + } + Atom::DataPropertyAtom { ref args, .. } => { + let (d1, d2) = args; + if let DArgument::Variable(ref var) = d1 { + vars.insert(var.clone()); + } + if let DArgument::Variable(ref var) = d2 { + vars.insert(var.clone()); + } + } + Atom::DataRangeAtom { ref arg, .. } => { + if let DArgument::Variable(ref var) = arg { + vars.insert(var.clone()); + } + } + Atom::DifferentIndividualsAtom(ref i1, ref i2) + | Atom::SameIndividualAtom(ref i1, ref i2) => { + if let IArgument::Variable(ref var) = i1 { + vars.insert(var.clone()); + } + if let IArgument::Variable(ref var) = i2 { + vars.insert(var.clone()); + } + } + Atom::ObjectPropertyAtom { ref args, .. } => { + let (i1, i2) = args; + if let IArgument::Variable(ref var) = i1 { + vars.insert(var.clone()); + } + if let IArgument::Variable(ref var) = i2 { + vars.insert(var.clone()); + } + } + } + } - thick_triple + vars } diff --git a/src/import/mod.rs b/src/import/mod.rs index 07fecca..d828d68 100644 --- a/src/import/mod.rs +++ b/src/import/mod.rs @@ -1 +1,4 @@ +mod curify; +mod db; pub mod import; +mod triple; diff --git a/src/import/triple.rs b/src/import/triple.rs new file mode 100644 index 0000000..4a51483 --- /dev/null +++ b/src/import/triple.rs @@ -0,0 +1,153 @@ +use anyhow::{Context, Result}; +use serde_json::Value; + +// LDTab datatypes +pub(crate) const DATATYPE_IRI: &str = "_IRI"; + +// Default values +pub(crate) const DEFAULT_GRAPH: &str = "graph"; +pub(crate) const UNKNOWN_VALUE: &str = ""; + +#[derive(Debug, Clone)] +pub(crate) struct LdTabTriple { + pub assertion: i32, + pub retraction: i32, + pub graph: String, + pub subject: Value, + pub predicate: Value, + pub object: Value, + pub datatype: Value, + pub annotation: Value, +} + +impl LdTabTriple { + /// Create a new triple with default assertion=1, retraction=0, default graph. + pub fn new( + subject: impl Into, + predicate: impl Into, + object: impl Into, + datatype: impl Into, + ) -> Self { + Self { + assertion: 1, + retraction: 0, + graph: DEFAULT_GRAPH.to_string(), + subject: subject.into(), + predicate: predicate.into(), + object: object.into(), + datatype: datatype.into(), + annotation: Value::Null, + } + } + + pub fn annotation(mut self, annotation: impl Into) -> Self { + self.annotation = annotation.into(); + self + } +} + +/// Parse a JSON thick-triple (from wiring_rs) into an LdTabTriple. +pub(crate) fn ldtab_2_triple(value: &Value) -> Result { + let assertion = value + .get("assertion") + .and_then(|v| v.as_str()) + .and_then(|s| s.parse::().ok()) + .unwrap_or(1); + let retraction = value + .get("retraction") + .and_then(|v| v.as_str()) + .and_then(|s| s.parse::().ok()) + .unwrap_or(0); + + let graph = value + .get("graph") + .and_then(|v| v.as_str()) + .unwrap_or(DEFAULT_GRAPH) + .to_string(); + let subject = value + .get("subject") + .cloned() + .context("Thick triple missing 'subject' field")?; + let predicate = value + .get("predicate") + .cloned() + .context("Thick triple missing 'predicate' field")?; + let object = value + .get("object") + .cloned() + .context("Thick triple missing 'object' field")?; + let datatype = value + .get("datatype") + .cloned() + .context("Thick triple missing 'datatype' field")?; + let annotation = get_annotation(value); + + Ok(LdTabTriple { + assertion, + retraction, + graph, + subject, + predicate, + object, + datatype, + annotation, + }) +} + +pub fn is_ldtab_blanknode(input: &Value) -> bool { + input + .as_str() + .map(|s| s.starts_with(" (Value, Value) { + match value { + Value::String(_) => (value.clone(), default_datatype.clone()), + Value::Array(a) if !a.is_empty() => { + if let Some(x) = a[0].as_object() { + if let (Some(datatype), Some(obj)) = (x.get("datatype"), x.get("object")) { + return (obj.clone(), datatype.clone()); + } + } + (value.clone(), default_datatype.clone()) + } + Value::Object(x) => { + if x.get("datatype").and_then(|v| v.as_str()) == Some(DATATYPE_IRI) { + if let Some(obj) = x.get("object") { + return (obj.clone(), Value::String(DATATYPE_IRI.to_string())); + } + } + (value.clone(), default_datatype.clone()) + } + _ => (value.clone(), default_datatype.clone()), + } +} + +/// Serialize a Value to the string representation used in the database. +pub(crate) fn value_to_db_string(value: &Value) -> String { + match value { + Value::String(s) => s.clone(), + Value::Null => String::new(), + _ => value.to_string(), + } +} + +fn get_annotation(value: &Value) -> Value { + match value.get("annotation") { + None | Some(Value::Null) => Value::Null, + + Some(Value::String(s)) => { + if s.is_empty() { + Value::Null + } else if let Ok(inner) = serde_json::from_str::(s) { + inner + } else { + Value::String(s.clone()) + } + } + + Some(Value::Object(map)) if map.is_empty() => Value::Null, + Some(v) => v.clone(), + } +} diff --git a/src/init/init.rs b/src/init/init.rs new file mode 100644 index 0000000..ab0e113 --- /dev/null +++ b/src/init/init.rs @@ -0,0 +1,80 @@ +use anyhow::{Context, Result}; +use clap::ArgMatches; +use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; +use std::fs::File; +use std::path::Path; + +pub async fn init(sub_matches: &ArgMatches) -> Result<()> { + let input = sub_matches.get_one::("input"); + let connection = sub_matches.get_one::("connection"); + + let database = match (input, connection) { + (Some(input), None) => input, + (None, Some(connection)) => connection, + (None, None) => { + anyhow::bail!("You must provide either a database name or a database connection.") + } + //this case should not happen (clap should prevent it - see 'conflicts_with' in clap definition) + (Some(_), Some(_)) => { + anyhow::bail!("Both input and connection are provided, but only one should be.") + } + }; + + //TODO: this currently only works for SQLite + create_database(&database).await?; + + Ok(()) +} + +async fn create_database(database: &str) -> Result { + // Check if the database file already exists + if !Path::new(database).exists() { + println!( + "Database file does not exist. Creating database file at {}...", + database + ); + File::create(database) + .with_context(|| format!("Failed to create database file at {}", database))?; + } else { + println!("Database file already exists at {}.", database); + } + + let pool = SqlitePoolOptions::new() + .max_connections(5) + .connect(database) + .await + .context("Failed to connect to the database")?; + + // Create tables ldtab, prefix, and statement + sqlx::query( + r#" + BEGIN TRANSACTION; + CREATE TABLE ldtab( + 'key' TEXT PRIMARY KEY, + 'value' TEXT + ); + INSERT INTO ldtab VALUES('ldtab version','0.0.1'); + INSERT INTO ldtab VALUES('schema version','0'); + CREATE TABLE prefix ( + 'prefix' TEXT PRIMARY KEY, + 'base' TEXT NOT NULL + ); + CREATE TABLE statement ( + 'assertion' INTEGER NOT NULL, + 'retraction' INTEGER NOT NULL DEFAULT 0, + 'graph' TEXT NOT NULL, + 'subject' TEXT NOT NULL, + 'predicate' TEXT NOT NULL, + 'object' TEXT NOT NULL, + 'datatype' TEXT NOT NULL, + 'annotation' TEXT + ); + COMMIT; + "#, + ) + .execute(&pool) + .await + .context("Failed to create the table")?; + + Ok(pool) +} diff --git a/src/init/mod.rs b/src/init/mod.rs new file mode 100644 index 0000000..43763f1 --- /dev/null +++ b/src/init/mod.rs @@ -0,0 +1 @@ +pub mod init; diff --git a/src/lib.rs b/src/lib.rs index 5f5ff9c..804b981 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ -pub mod xml; -pub mod owl_2_ofn; -pub mod ofn_2_owl; pub mod import; +pub mod init; +pub mod ofn_2_owl; +pub mod owl_2_ofn; +pub mod prefix; +pub mod xml; diff --git a/src/main.rs b/src/main.rs index 2d88355..cd8d4f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,66 +1,112 @@ -use horned_owl::command::parse_path; -use std::path::Path; +use anyhow::Result; +use clap::{arg, command, value_parser, Arg, ArgMatches, Command}; use std::env; -use horned_owl::ontology::set::SetOntology; -use horned_owl::model::*; -use serde_json::{Value}; +use tokio; -//use rio_xml::{RdfXmlParser, RdfXmlError}; -//use rio_api::parser::TriplesParser; -//use rio_api::model::*; -//use std::fs::File; -//use im::{HashSet, hashset}; - -pub mod xml; -pub mod owl_2_ofn; -pub mod ofn_2_owl; +pub mod export; pub mod import; +pub mod init; +pub mod ofn_2_owl; +pub mod owl_2_ofn; +pub mod prefix; +pub mod xml; extern crate wiring_rs; -pub mod round; - - -fn main() { - - let args: Vec = env::args().collect(); +#[tokio::main] +async fn main() -> Result<()> { + let matches = parse_arguments(); - match parse_path(Path::new(&args[1])) { - Ok(parsed) => { - let ont = &parsed.decompose().0; - - demo(ont); - + let exit_result = match matches.subcommand() { + Some(("init", sub_matches)) => { + init::init::init(sub_matches).await?; + Ok(()) } - Err(error) => { dbg!("ERROR: {}", error); } - } -} - -fn demo(ontology : &SetOntology) { - - let id = ontology.id(); - let iri = id.clone().iri.unwrap(); - let iri_value = Value::String(String::from(iri.get(0..).unwrap())); - - for ann_axiom in ontology.iter() { - println!("Horned OWL: {:?}" , ann_axiom); - - //1. translate Horned OWL to OFN S-expression - let ofn = owl_2_ofn::transducer::translate(ann_axiom); - let ofn = - match ofn[0].as_str() { - Some("Import") => Value::Array(vec![ofn[0].clone(), iri_value.clone(), ofn[1].clone()]), - Some("OntologyAnnotation") => Value::Array(vec![ofn[0].clone(), iri_value.clone(), ofn[1].clone()]) , - _ => ofn.clone() - }; - println!("OFN S-expression: {}" , ofn); + Some(("prefix", sub_matches)) => { + prefix::prefix::prefix(sub_matches).await?; + Ok(()) + } + Some(("import", sub_matches)) => { + import::import::import(sub_matches).await?; + Ok(()) + } + Some(("export", sub_matches)) => { + export::export::export(sub_matches).await?; + Ok(()) + } + _ => anyhow::bail!("Unrecognized or missing subcommand."), + }; - //2. translate OFN S-Expression to LDTab ThickTriple - let ldtab = wiring_rs::ofn_2_ldtab::translation::ofn_2_thick_triple(&ofn); - println!("LDTab: {}" , ldtab); - - //3. Get typing and labeling information from ontology - //4. Use wiring to type and label expressions - //5. use ofn_2_ofn module to translate OWL axioms back into Horned OWL + exit_result +} - } +fn parse_arguments() -> ArgMatches { + command!() + .about("This application is a CLI for converting ontologies") // requires `cargo` feature + .subcommand_required(true) + .subcommand( + Command::new("init") + .about("Initialises LDTab database") + .arg( + Arg::new("input") + .index(1) // positional arguments can't have a short/long name + .help("The input file to process") + .conflicts_with("connection"), + ) + .arg( + arg!( + -c --connection "Specifies a database connection URL or file" + ) + .required(false) + .value_parser(value_parser!(String)), + ), + ) + .subcommand( + Command::new("prefix") + .about("Configure prefixes for an LDTab database") + .arg( + Arg::new("database") + .index(1) // The position of the argument in the command line + .required(true) + .help("The database"), + ) + .arg( + Arg::new("prefixes") + .index(2) // The position of the argument in the command line + .required(true) + .help("The prefix TSV file"), + ), + ) + .subcommand( + Command::new("import") + .about("Imports an LDTab database") + .arg( + Arg::new("database") + .index(1) // The position of the argument in the command line + .required(true) + .help("The target database to import into"), + ) + .arg( + Arg::new("ontology") + .index(2) // The position of the argument in the command line + .required(true) + .help("The ontology to be imported"), + ), + ) + .subcommand( + Command::new("export") + .about("Exports an LDTab database") + .arg( + Arg::new("database") + .index(1) // The position of the argument in the command line + .required(true) + .help("The datapase to export"), + ) + .arg( + Arg::new("output") + .index(2) // The position of the argument in the command line + .required(true) + .help("The target file to export to"), + ), + ) + .get_matches() } diff --git a/src/ofn_2_owl/annotation_transducer.rs b/src/ofn_2_owl/annotation_transducer.rs index be53e4e..07f4013 100644 --- a/src/ofn_2_owl/annotation_transducer.rs +++ b/src/ofn_2_owl/annotation_transducer.rs @@ -1,47 +1,41 @@ -use serde_json::{Value}; -use horned_owl::model::{Build, Annotation, AnnotationProperty, AnnotationValue, AnnotationSubject, RcStr}; -use crate::ofn_2_owl::expression_transducer as expression_transducer; -use crate::ofn_2_owl::util as util; +use anyhow::Result; +use crate::ofn_2_owl::expression_transducer; +use crate::ofn_2_owl::util; +use crate::ofn_2_owl::util::{build, extract_iri_str}; +use horned_owl::model::{ + Annotation, AnnotationProperty, AnnotationSubject, AnnotationValue, ArcStr, +}; +use serde_json::Value; -pub fn translate_annotation(v : &Value) -> Annotation { - let property = translate_annotation_property(&v[1]); - let value = translate_annotation_value(&v[2]); - let annotation = Annotation{ap : property, - av : value}; - annotation +pub fn translate_annotation(v: &Value) -> Result> { + let property = translate_annotation_property(&v[1])?; + let value = translate_annotation_value(&v[2])?; + Ok(Annotation { + ap: property, + av: value, + }) } -pub fn translate_annotation_property(v : &Value) -> AnnotationProperty { - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - b.annotation_property(iri.clone()) +pub fn translate_annotation_property(v: &Value) -> Result> { + Ok(build().annotation_property(extract_iri_str(v)?)) } -pub fn translate_annotation_value(v : &Value) -> AnnotationValue { +pub fn translate_annotation_value(v: &Value) -> Result> { if util::is_literal(v) { - let value = expression_transducer::translate_literal(v); - AnnotationValue::Literal(value) - } else { - let b = Build::new(); - let string = v.as_str().unwrap(); - let iri = b.iri(string); - AnnotationValue::IRI(iri) - } + let value = expression_transducer::translate_literal(v)?; + Ok(AnnotationValue::Literal(value)) + } else { + let iri = build().iri(extract_iri_str(v)?); + Ok(AnnotationValue::IRI(iri)) + } } -pub fn translate_annotation_subject(v : &Value) -> AnnotationSubject { +pub fn translate_annotation_subject(v: &Value) -> Result> { if util::is_anonynous_individual(v) { - let individual = expression_transducer::translate_anonymous_individual(v); - AnnotationSubject::AnonymousIndividual(individual) - } else { - let b = Build::new(); - let string = v.as_str().unwrap(); - let iri = b.iri(string); - AnnotationSubject::IRI(iri) - } + let individual = expression_transducer::translate_anonymous_individual(v)?; + Ok(AnnotationSubject::AnonymousIndividual(individual)) + } else { + let iri = build().iri(extract_iri_str(v)?); + Ok(AnnotationSubject::IRI(iri)) + } } diff --git a/src/ofn_2_owl/axiom_transducer.rs b/src/ofn_2_owl/axiom_transducer.rs index c2d5518..80c17d5 100644 --- a/src/ofn_2_owl/axiom_transducer.rs +++ b/src/ofn_2_owl/axiom_transducer.rs @@ -1,10 +1,26 @@ -use serde_json::{Value}; -use crate::ofn_2_owl::expression_transducer as expression_transducer; -use crate::ofn_2_owl::annotation_transducer as annotation_transducer; -use horned_owl::model::{Annotation, Individual, AnnotationProperty, Datatype, NamedIndividual, DataProperty, ObjectProperty, Build, Class, ClassExpression, Axiom, SubClassOf, ClassAssertion, DeclareClass, DeclareObjectProperty, DeclareDatatype, DeclareDataProperty, DeclareNamedIndividual, DisjointClasses, DisjointUnion, EquivalentClasses, EquivalentObjectProperties, ObjectPropertyDomain, ObjectPropertyExpression, SubObjectPropertyOf, TransitiveObjectProperty, ObjectPropertyAssertion, ReflexiveObjectProperty, IrreflexiveObjectProperty, SymmetricObjectProperty, AsymmetricObjectProperty, ObjectPropertyRange, InverseObjectProperties, FunctionalObjectProperty, InverseFunctionalObjectProperty, DisjointObjectProperties, Import, SubDataPropertyOf, EquivalentDataProperties, DisjointDataProperties, DataPropertyDomain, DataPropertyRange, FunctionalDataProperty, DatatypeDefinition, SameIndividual, DifferentIndividuals, NegativeObjectPropertyAssertion, DataPropertyAssertion, NegativeDataPropertyAssertion, AnnotationAssertion, DeclareAnnotationProperty, SubAnnotationPropertyOf, AnnotationPropertyDomain, AnnotationPropertyRange, RcStr}; - -pub fn translate_axiom(v : &Value) -> Axiom { - +use anyhow::{bail, Context, Result}; +use crate::ofn_2_owl::annotation_transducer; +use crate::ofn_2_owl::expression_transducer; +use crate::ofn_2_owl::util::{build, extract_iri_str}; +use horned_owl::model::{ + Annotation, AnnotationAssertion, AnnotationProperty, AnnotationPropertyDomain, + AnnotationPropertyRange, AsymmetricObjectProperty, Class, ClassAssertion, + ClassExpression, Component, DataProperty, DataPropertyAssertion, DataPropertyDomain, + DataPropertyRange, DatatypeDefinition, DeclareAnnotationProperty, DeclareClass, + DeclareDataProperty, DeclareDatatype, DeclareNamedIndividual, DeclareObjectProperty, + DifferentIndividuals, DisjointClasses, DisjointDataProperties, DisjointObjectProperties, + DisjointUnion, EquivalentClasses, EquivalentDataProperties, EquivalentObjectProperties, + FunctionalDataProperty, FunctionalObjectProperty, Import, Individual, + InverseFunctionalObjectProperty, InverseObjectProperties, IrreflexiveObjectProperty, + NamedIndividual, NegativeDataPropertyAssertion, NegativeObjectPropertyAssertion, + ObjectProperty, ObjectPropertyAssertion, ObjectPropertyDomain, ObjectPropertyExpression, + ObjectPropertyRange, ArcStr, ReflexiveObjectProperty, SameIndividual, SubAnnotationPropertyOf, + SubClassOf, SubDataPropertyOf, SubObjectPropertyOf, SymmetricObjectProperty, + TransitiveObjectProperty, +}; +use serde_json::Value; + +pub fn translate_axiom(v: &Value) -> Result> { match v[0].as_str() { //Ontology annotation //import @@ -35,8 +51,7 @@ pub fn translate_axiom(v : &Value) -> Axiom { Some("FunctionalDataProperty") => translate_functional_data_property(v), Some("DatatypeDefinition") => translate_datatype_definition(v), - //Some("HasKey") => translate_has_key(v), //no property type support in OFN S - Some("HasKey") => panic!("HasKey operator currently not supported"), + Some("HasKey") => bail!("HasKey operator currently not supported"), Some("SameIndividual") => translate_same_individual(v), Some("DifferentIndividuals") => translate_different_individuals(v), @@ -53,456 +68,426 @@ pub fn translate_axiom(v : &Value) -> Axiom { Some("AnnotationPropertyRange") => translate_annotation_property_range(v), Some("Import") => translate_import(v), - Some(_) => panic!("Not a valid OWL axiom operator"), - None => panic!("Not a valid (typed) OFN S-expression"), - } + Some(op) => bail!("Not a valid OWL axiom operator: {}", op), + None => bail!("Not a valid (typed) OFN S-expression: missing operator"), + } } -pub fn translate_named_class(v : &Value) -> Class { - - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - b.class(iri.clone()).into() +pub fn translate_named_class(v: &Value) -> Result> { + Ok(build().class(extract_iri_str(v)?).into()) } -pub fn translate_import(v : &Value) -> Axiom { - - let b = Build::new(); +pub fn translate_import(v: &Value) -> Result> { + let import = build().iri(extract_iri_str(&v[2])?).into(); - let _ontology_iri = match v[1].clone() { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - let import_iri = match v[2].clone() { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - //let ont = b.iri(ontology_iri).into(); - let import = b.iri(import_iri).into(); - - let axiom = Import(import) ; - Axiom::Import(axiom) + let axiom = Import(import); + Ok(Component::Import(axiom)) } -pub fn translate_object_property(v : &Value) -> ObjectProperty { - - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - b.object_property(iri.clone()).into() +pub fn translate_object_property(v: &Value) -> Result> { + Ok(build().object_property(extract_iri_str(v)?).into()) } - -pub fn translate_annotation_property(v : &Value) -> AnnotationProperty { - - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - b.annotation_property(iri.clone()).into() +pub fn translate_annotation_property(v: &Value) -> Result> { + Ok(build().annotation_property(extract_iri_str(v)?).into()) } -pub fn translate_named_individual(v : &Value) -> NamedIndividual { - - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - b.named_individual(iri.clone()).into() +pub fn translate_named_individual(v: &Value) -> Result> { + Ok(build().named_individual(extract_iri_str(v)?).into()) } -//TODO refactor this into expression_transducer +//TODO refactor this into expression_transducer -pub fn translate_subclass_of(v : &Value) -> Axiom { - let sub = expression_transducer::translate_class_expression(&v[1]); - let sup = expression_transducer::translate_class_expression(&v[2]); - let axiom = SubClassOf{sub : sub, - sup : sup}; - Axiom::SubClassOf(axiom) +pub fn translate_subclass_of(v: &Value) -> Result> { + let sub = expression_transducer::translate_class_expression(&v[1])?; + let sup = expression_transducer::translate_class_expression(&v[2])?; + let axiom = SubClassOf { sub, sup }; + Ok(Component::SubClassOf(axiom)) } -pub fn translate_equivalent_classes(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_class_expression(&x)) - .collect(); - let axiom = EquivalentClasses{0 : operands}; - Axiom::EquivalentClasses(axiom) +pub fn translate_equivalent_classes(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for EquivalentClasses")?[1..] + .iter() + .map(|x| expression_transducer::translate_class_expression(x)) + .collect::>()?; + let axiom = EquivalentClasses(operands); + Ok(Component::EquivalentClasses(axiom)) } -pub fn translate_disjoint_classes(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_class_expression(&x)) - .collect(); - let axiom = DisjointClasses{0 : operands}; - Axiom::DisjointClasses(axiom) +pub fn translate_disjoint_classes(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for DisjointClasses")?[1..] + .iter() + .map(|x| expression_transducer::translate_class_expression(x)) + .collect::>()?; + let axiom = DisjointClasses(operands); + Ok(Component::DisjointClasses(axiom)) } -pub fn translate_disjoint_union(v : &Value) -> Axiom { +pub fn translate_disjoint_union(v: &Value) -> Result> { + let lhs = translate_named_class(&v[1])?; - //NB: we need a (named) class here - not a class expression - //let lhs = expression_transducer::translate_class_expression(&v[1]); - let lhs = translate_named_class(&v[1]); - - let operands: Vec> = (&(v.as_array().unwrap())[2..]) - .into_iter() - .map(|x| expression_transducer::translate_class_expression(&x)) - .collect(); - let axiom = DisjointUnion{0: lhs, - 1 : operands}; - Axiom::DisjointUnion(axiom) + let operands: Vec> = v.as_array() + .context("Expected array for DisjointUnion")?[2..] + .iter() + .map(|x| expression_transducer::translate_class_expression(x)) + .collect::>()?; + let axiom = DisjointUnion(lhs, operands); + Ok(Component::DisjointUnion(axiom)) } -pub fn translate_class_declaration(v : &Value) -> Axiom { - - let class = translate_named_class(&v[1]); - let axiom = DeclareClass{0: class}; - Axiom::DeclareClass(axiom) +pub fn translate_class_declaration(v: &Value) -> Result> { + let class = translate_named_class(&v[1])?; + let axiom = DeclareClass(class); + Ok(Component::DeclareClass(axiom)) } -pub fn translate_object_property_declaration(v : &Value) -> Axiom { - - let property = translate_object_property(&v[1]); - let axiom = DeclareObjectProperty{0: property}; - Axiom::DeclareObjectProperty(axiom) +pub fn translate_object_property_declaration(v: &Value) -> Result> { + let property = translate_object_property(&v[1])?; + let axiom = DeclareObjectProperty(property); + Ok(Component::DeclareObjectProperty(axiom)) } -pub fn translate_data_property_declaration(v : &Value) -> Axiom { - - let property = expression_transducer::translate_data_property(&v[1]); - let axiom = DeclareDataProperty{0: property}; - Axiom::DeclareDataProperty(axiom) +pub fn translate_data_property_declaration(v: &Value) -> Result> { + let property = expression_transducer::translate_data_property(&v[1])?; + let axiom = DeclareDataProperty(property); + Ok(Component::DeclareDataProperty(axiom)) } -pub fn translate_annotation_property_declaration(v : &Value) -> Axiom { - - let property = translate_annotation_property(&v[1]); - let axiom = DeclareAnnotationProperty{0: property}; - Axiom::DeclareAnnotationProperty(axiom) +pub fn translate_annotation_property_declaration(v: &Value) -> Result> { + let property = translate_annotation_property(&v[1])?; + let axiom = DeclareAnnotationProperty(property); + Ok(Component::DeclareAnnotationProperty(axiom)) } -pub fn translate_named_individual_declaration(v : &Value) -> Axiom { - - let individual = translate_named_individual(&v[1]); - let axiom = DeclareNamedIndividual{0: individual}; - Axiom::DeclareNamedIndividual(axiom) +pub fn translate_named_individual_declaration(v: &Value) -> Result> { + let individual = translate_named_individual(&v[1])?; + let axiom = DeclareNamedIndividual(individual); + Ok(Component::DeclareNamedIndividual(axiom)) } -pub fn translate_datatype_declaration(v : &Value) -> Axiom { - - let datatype = expression_transducer::translate_datatype(&v[1]); - let axiom = DeclareDatatype{0: datatype}; - Axiom::DeclareDatatype(axiom) +pub fn translate_datatype_declaration(v: &Value) -> Result> { + let datatype = expression_transducer::translate_datatype(&v[1])?; + let axiom = DeclareDatatype(datatype); + Ok(Component::DeclareDatatype(axiom)) } -pub fn translate_declaration(v : &Value) -> Axiom { - let unwrapped_declaration = v[1].clone(); - match unwrapped_declaration[0].as_str() { - Some("Class") => translate_class_declaration(&unwrapped_declaration), - Some("ObjectProperty") => translate_object_property_declaration(&unwrapped_declaration), - Some("DataProperty") => translate_data_property_declaration(&unwrapped_declaration), - Some("AnnotationProperty") => translate_annotation_property_declaration(&unwrapped_declaration), - Some("NamedIndividual") => translate_named_individual_declaration(&unwrapped_declaration), - Some("Datatype") => translate_datatype_declaration(&unwrapped_declaration), - _ => panic!() +pub fn translate_declaration(v: &Value) -> Result> { + let declaration = &v[1]; + match declaration[0].as_str() { + Some("Class") => translate_class_declaration(declaration), + Some("ObjectProperty") => translate_object_property_declaration(declaration), + Some("DataProperty") => translate_data_property_declaration(declaration), + Some("AnnotationProperty") => translate_annotation_property_declaration(declaration), + Some("NamedIndividual") => translate_named_individual_declaration(declaration), + Some("Datatype") => translate_datatype_declaration(declaration), + _ => bail!("Unknown declaration type"), } } -pub fn translate_sub_object_property_of(v : &Value) -> Axiom { - let lhs = expression_transducer::translate_sub_object_property_expression(&v[1]); - let rhs = expression_transducer::translate_object_property_expression(&v[2]); - let axiom = SubObjectPropertyOf{sub : lhs, - sup: rhs}; - Axiom::SubObjectPropertyOf(axiom) +pub fn translate_sub_object_property_of(v: &Value) -> Result> { + let lhs = expression_transducer::translate_sub_object_property_expression(&v[1])?; + let rhs = expression_transducer::translate_object_property_expression(&v[2])?; + let axiom = SubObjectPropertyOf { sub: lhs, sup: rhs }; + Ok(Component::SubObjectPropertyOf(axiom)) } -pub fn translate_equivalent_object_properties(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_object_property_expression(&x)) - .collect(); +pub fn translate_equivalent_object_properties(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for EquivalentObjectProperties")?[1..] + .iter() + .map(|x| expression_transducer::translate_object_property_expression(x)) + .collect::>()?; let axiom = EquivalentObjectProperties(operands); - Axiom::EquivalentObjectProperties(axiom) + Ok(Component::EquivalentObjectProperties(axiom)) } -pub fn translate_disjoint_object_properties(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_object_property_expression(&x)) - .collect(); +pub fn translate_disjoint_object_properties(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for DisjointObjectProperties")?[1..] + .iter() + .map(|x| expression_transducer::translate_object_property_expression(x)) + .collect::>()?; let axiom = DisjointObjectProperties(operands); - Axiom::DisjointObjectProperties(axiom) + Ok(Component::DisjointObjectProperties(axiom)) } -pub fn translate_inverse_object_properties(v : &Value) -> Axiom { - let lhs = translate_object_property(&v[1]); - let rhs = translate_object_property(&v[2]); - let axiom = InverseObjectProperties { 0: lhs, 1: rhs}; - Axiom::InverseObjectProperties(axiom) +pub fn translate_inverse_object_properties(v: &Value) -> Result> { + let lhs = translate_object_property(&v[1])?; + let rhs = translate_object_property(&v[2])?; + let axiom = InverseObjectProperties(lhs, rhs); + Ok(Component::InverseObjectProperties(axiom)) } -pub fn translate_object_property_domain(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let domain = expression_transducer::translate_class_expression(&v[2]); - let axiom = ObjectPropertyDomain{ope: property, ce : domain }; - Axiom::ObjectPropertyDomain(axiom) +pub fn translate_object_property_domain(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let domain = expression_transducer::translate_class_expression(&v[2])?; + let axiom = ObjectPropertyDomain { + ope: property, + ce: domain, + }; + Ok(Component::ObjectPropertyDomain(axiom)) } -pub fn translate_object_property_range(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let domain = expression_transducer::translate_class_expression(&v[2]); - let axiom = ObjectPropertyRange{ope: property, ce : domain }; - Axiom::ObjectPropertyRange(axiom) +pub fn translate_object_property_range(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let range = expression_transducer::translate_class_expression(&v[2])?; + let axiom = ObjectPropertyRange { + ope: property, + ce: range, + }; + Ok(Component::ObjectPropertyRange(axiom)) } -pub fn translate_functional_object_property(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let axiom = FunctionalObjectProperty{0: property}; - Axiom::FunctionalObjectProperty(axiom) +pub fn translate_functional_object_property(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let axiom = FunctionalObjectProperty(property); + Ok(Component::FunctionalObjectProperty(axiom)) } -pub fn translate_inverse_functional_object_property(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let axiom = InverseFunctionalObjectProperty{0: property}; - Axiom::InverseFunctionalObjectProperty(axiom) +pub fn translate_inverse_functional_object_property(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let axiom = InverseFunctionalObjectProperty(property); + Ok(Component::InverseFunctionalObjectProperty(axiom)) } -pub fn translate_reflexive_object_property(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let axiom = ReflexiveObjectProperty{0: property}; - Axiom::ReflexiveObjectProperty(axiom) +pub fn translate_reflexive_object_property(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let axiom = ReflexiveObjectProperty(property); + Ok(Component::ReflexiveObjectProperty(axiom)) } -pub fn translate_irreflexive_object_property(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let axiom = IrreflexiveObjectProperty{0: property}; - Axiom::IrreflexiveObjectProperty(axiom) +pub fn translate_irreflexive_object_property(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let axiom = IrreflexiveObjectProperty(property); + Ok(Component::IrreflexiveObjectProperty(axiom)) } -pub fn translate_symmetric_object_property(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let axiom = SymmetricObjectProperty{0: property}; - Axiom::SymmetricObjectProperty(axiom) +pub fn translate_symmetric_object_property(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let axiom = SymmetricObjectProperty(property); + Ok(Component::SymmetricObjectProperty(axiom)) } -pub fn translate_asymmetric_object_property(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let axiom = AsymmetricObjectProperty{0: property}; - Axiom::AsymmetricObjectProperty(axiom) +pub fn translate_asymmetric_object_property(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let axiom = AsymmetricObjectProperty(property); + Ok(Component::AsymmetricObjectProperty(axiom)) } -pub fn translate_transitive_object_property(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let axiom = TransitiveObjectProperty{0: property}; - Axiom::TransitiveObjectProperty(axiom) +pub fn translate_transitive_object_property(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let axiom = TransitiveObjectProperty(property); + Ok(Component::TransitiveObjectProperty(axiom)) } -pub fn translate_sub_dataproperty_of(v : &Value) -> Axiom { - let lhs = expression_transducer::translate_data_property(&v[1]); - let rhs = expression_transducer::translate_data_property(&v[2]); +pub fn translate_sub_dataproperty_of(v: &Value) -> Result> { + let lhs = expression_transducer::translate_data_property(&v[1])?; + let rhs = expression_transducer::translate_data_property(&v[2])?; - let axiom = SubDataPropertyOf{sub : lhs, sup : rhs}; - Axiom::SubDataPropertyOf(axiom) + let axiom = SubDataPropertyOf { sub: lhs, sup: rhs }; + Ok(Component::SubDataPropertyOf(axiom)) } -pub fn translate_equivalent_data_properties(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_data_property(&x)) - .collect(); +pub fn translate_equivalent_data_properties(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for EquivalentDataProperties")?[1..] + .iter() + .map(|x| expression_transducer::translate_data_property(x)) + .collect::>()?; let axiom = EquivalentDataProperties(operands); - Axiom::EquivalentDataProperties(axiom) + Ok(Component::EquivalentDataProperties(axiom)) } -pub fn translate_disjoint_data_properties(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_data_property(&x)) - .collect(); +pub fn translate_disjoint_data_properties(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for DisjointDataProperties")?[1..] + .iter() + .map(|x| expression_transducer::translate_data_property(x)) + .collect::>()?; let axiom = DisjointDataProperties(operands); - Axiom::DisjointDataProperties(axiom) - + Ok(Component::DisjointDataProperties(axiom)) } -pub fn translate_data_property_domain(v : &Value) -> Axiom { - let property = expression_transducer::translate_data_property(&v[1]); - let domain = expression_transducer::translate_class_expression(&v[2]); - - let axiom = DataPropertyDomain{dp: property, ce: domain}; - Axiom::DataPropertyDomain(axiom) +pub fn translate_data_property_domain(v: &Value) -> Result> { + let property = expression_transducer::translate_data_property(&v[1])?; + let domain = expression_transducer::translate_class_expression(&v[2])?; + let axiom = DataPropertyDomain { + dp: property, + ce: domain, + }; + Ok(Component::DataPropertyDomain(axiom)) } -pub fn translate_data_property_range(v : &Value) -> Axiom { - let property = expression_transducer::translate_data_property(&v[1]); - let range = expression_transducer::translate_data_range(&v[2]); +pub fn translate_data_property_range(v: &Value) -> Result> { + let property = expression_transducer::translate_data_property(&v[1])?; + let range = expression_transducer::translate_data_range(&v[2])?; - let axiom = DataPropertyRange{dp: property, dr: range}; - Axiom::DataPropertyRange(axiom) + let axiom = DataPropertyRange { + dp: property, + dr: range, + }; + Ok(Component::DataPropertyRange(axiom)) } -pub fn translate_functional_data_property(v : &Value) -> Axiom { - - let property = expression_transducer::translate_data_property(&v[1]); +pub fn translate_functional_data_property(v: &Value) -> Result> { + let property = expression_transducer::translate_data_property(&v[1])?; - let axiom = FunctionalDataProperty{0: property}; - Axiom::FunctionalDataProperty(axiom) + let axiom = FunctionalDataProperty(property); + Ok(Component::FunctionalDataProperty(axiom)) } -pub fn translate_datatype_definition(v : &Value) -> Axiom { - - let kind = expression_transducer::translate_datatype(&v[1]); - let range = expression_transducer::translate_data_range(&v[2]); - - let axiom = DatatypeDefinition{kind : kind, range : range}; - Axiom::DatatypeDefinition(axiom) +pub fn translate_datatype_definition(v: &Value) -> Result> { + let kind = expression_transducer::translate_datatype(&v[1])?; + let range = expression_transducer::translate_data_range(&v[2])?; + let axiom = DatatypeDefinition { kind, range }; + Ok(Component::DatatypeDefinition(axiom)) } //TODO cannot translate this without knowing the type of the property arguments .. -//pub fn translate_has_key(v : &Value) -> Axiom { +//pub fn translate_has_key(v : &Value) -> Component { // // let class_expression = expression_transducer::translate_class_expression(&v[1]); // let operands: Vec = (&(v.as_array().unwrap())[1..]) // .into_iter() // .map(|x| expression_transducer::translate_data_property(&x)) -// .collect(); +// .collect(); // //} -pub fn translate_same_individual(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_individual(&x)) - .collect(); +pub fn translate_same_individual(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for SameIndividual")?[1..] + .iter() + .map(|x| expression_transducer::translate_individual(x)) + .collect::>()?; - let axiom = SameIndividual{0 : operands}; - Axiom::SameIndividual(axiom) + let axiom = SameIndividual(operands); + Ok(Component::SameIndividual(axiom)) } +pub fn translate_different_individuals(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for DifferentIndividuals")?[1..] + .iter() + .map(|x| expression_transducer::translate_individual(x)) + .collect::>()?; -pub fn translate_different_individuals(v : &Value) -> Axiom { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| expression_transducer::translate_individual(&x)) - .collect(); - - let axiom = DifferentIndividuals{0 : operands}; - Axiom::DifferentIndividuals(axiom) + let axiom = DifferentIndividuals(operands); + Ok(Component::DifferentIndividuals(axiom)) } +pub fn translate_class_assertion(v: &Value) -> Result> { + let class_expression = expression_transducer::translate_class_expression(&v[1])?; + let individual = expression_transducer::translate_individual(&v[2])?; -pub fn translate_class_assertion(v : &Value) -> Axiom { - - let class_expression = expression_transducer::translate_class_expression(&v[1]); - let individual = expression_transducer::translate_individual(&v[2]); - - let axiom = ClassAssertion{ce: class_expression, i: individual}; - Axiom::ClassAssertion(axiom) + let axiom = ClassAssertion { + ce: class_expression, + i: individual, + }; + Ok(Component::ClassAssertion(axiom)) } -pub fn translate_object_property_assertion(v : &Value) -> Axiom { - - let property = expression_transducer::translate_object_property_expression(&v[1]); - let from = expression_transducer::translate_individual(&v[2]); - let to = expression_transducer::translate_individual(&v[3]); +pub fn translate_object_property_assertion(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let from = expression_transducer::translate_individual(&v[2])?; + let to = expression_transducer::translate_individual(&v[3])?; - let axiom = ObjectPropertyAssertion{ope: property, from: from, to: to}; - Axiom::ObjectPropertyAssertion(axiom) + let axiom = ObjectPropertyAssertion { + ope: property, + from, + to, + }; + Ok(Component::ObjectPropertyAssertion(axiom)) } -pub fn translate_negative_object_property_assertion(v : &Value) -> Axiom { - let property = expression_transducer::translate_object_property_expression(&v[1]); - let from = expression_transducer::translate_individual(&v[2]); - let to = expression_transducer::translate_individual(&v[3]); - - let axiom = NegativeObjectPropertyAssertion{ope: property, from: from, to: to}; - Axiom::NegativeObjectPropertyAssertion(axiom) +pub fn translate_negative_object_property_assertion(v: &Value) -> Result> { + let property = expression_transducer::translate_object_property_expression(&v[1])?; + let from = expression_transducer::translate_individual(&v[2])?; + let to = expression_transducer::translate_individual(&v[3])?; + let axiom = NegativeObjectPropertyAssertion { + ope: property, + from, + to, + }; + Ok(Component::NegativeObjectPropertyAssertion(axiom)) } -pub fn translate_data_property_assertion(v : &Value) -> Axiom { +pub fn translate_data_property_assertion(v: &Value) -> Result> { + let property = expression_transducer::translate_data_property(&v[1])?; + let from = expression_transducer::translate_individual(&v[2])?; + let to = expression_transducer::translate_literal(&v[3])?; - let property = expression_transducer::translate_data_property(&v[1]); - let from = expression_transducer::translate_individual(&v[2]); - let to = expression_transducer::translate_literal(&v[3]); - - let axiom = DataPropertyAssertion{dp: property, from: from, to: to}; - Axiom::DataPropertyAssertion(axiom) + let axiom = DataPropertyAssertion { + dp: property, + from, + to, + }; + Ok(Component::DataPropertyAssertion(axiom)) } -pub fn translate_negative_data_property_assertion(v : &Value) -> Axiom { - - let property = expression_transducer::translate_data_property(&v[1]); - let from = expression_transducer::translate_individual(&v[2]); - let to = expression_transducer::translate_literal(&v[3]); +pub fn translate_negative_data_property_assertion(v: &Value) -> Result> { + let property = expression_transducer::translate_data_property(&v[1])?; + let from = expression_transducer::translate_individual(&v[2])?; + let to = expression_transducer::translate_literal(&v[3])?; - let axiom = NegativeDataPropertyAssertion{dp: property, from: from, to: to}; - Axiom::NegativeDataPropertyAssertion(axiom) + let axiom = NegativeDataPropertyAssertion { + dp: property, + from, + to, + }; + Ok(Component::NegativeDataPropertyAssertion(axiom)) } -pub fn translate_annotation_assertion(v : &Value) -> Axiom { - - let property = annotation_transducer::translate_annotation_property(&v[1]); - let subject = annotation_transducer::translate_annotation_subject(&v[2]); - let value = annotation_transducer::translate_annotation_value(&v[3]); +pub fn translate_annotation_assertion(v: &Value) -> Result> { + let property = annotation_transducer::translate_annotation_property(&v[1])?; + let subject = annotation_transducer::translate_annotation_subject(&v[2])?; + let value = annotation_transducer::translate_annotation_value(&v[3])?; - let annotation = Annotation{ap : property, av :value}; + let annotation = Annotation { + ap: property, + av: value, + }; - let axiom = AnnotationAssertion{subject : subject, ann: annotation}; - Axiom::AnnotationAssertion(axiom) + let axiom = AnnotationAssertion { + subject, + ann: annotation, + }; + Ok(Component::AnnotationAssertion(axiom)) } -pub fn translate_sub_annotation_assertion(v : &Value) -> Axiom { - let sub = annotation_transducer::translate_annotation_property(&v[1]); - let sup = annotation_transducer::translate_annotation_property(&v[2]); +pub fn translate_sub_annotation_assertion(v: &Value) -> Result> { + let sub = annotation_transducer::translate_annotation_property(&v[1])?; + let sup = annotation_transducer::translate_annotation_property(&v[2])?; - let axiom = SubAnnotationPropertyOf{sub : sub, sup : sup }; - Axiom::SubAnnotationPropertyOf(axiom) + let axiom = SubAnnotationPropertyOf { sub, sup }; + Ok(Component::SubAnnotationPropertyOf(axiom)) } -pub fn translate_annotation_property_domain(v : &Value) -> Axiom { +pub fn translate_annotation_property_domain(v: &Value) -> Result> { + let property = annotation_transducer::translate_annotation_property(&v[1])?; - let property = annotation_transducer::translate_annotation_property(&v[1]); + let iri = build().iri(extract_iri_str(&v[2])?); - let b = Build::new(); - let string = v[2].as_str().unwrap(); - let iri = b.iri(string); - - let axiom = AnnotationPropertyDomain{ap : property, iri : iri}; - Axiom::AnnotationPropertyDomain(axiom) + let axiom = AnnotationPropertyDomain { + ap: property, + iri, + }; + Ok(Component::AnnotationPropertyDomain(axiom)) } -pub fn translate_annotation_property_range(v : &Value) -> Axiom { - - let property = annotation_transducer::translate_annotation_property(&v[1]); +pub fn translate_annotation_property_range(v: &Value) -> Result> { + let property = annotation_transducer::translate_annotation_property(&v[1])?; - let b = Build::new(); - let string = v[2].as_str().unwrap(); - let iri = b.iri(string); + let iri = build().iri(extract_iri_str(&v[2])?); - let axiom = AnnotationPropertyRange{ap : property, iri : iri}; - Axiom::AnnotationPropertyRange(axiom) + let axiom = AnnotationPropertyRange { + ap: property, + iri, + }; + Ok(Component::AnnotationPropertyRange(axiom)) } diff --git a/src/ofn_2_owl/expression_transducer.rs b/src/ofn_2_owl/expression_transducer.rs index 67a728f..2b79ad0 100644 --- a/src/ofn_2_owl/expression_transducer.rs +++ b/src/ofn_2_owl/expression_transducer.rs @@ -1,714 +1,407 @@ -use serde_json::{Value}; +use anyhow::{bail, Context, Result}; +use once_cell::sync::Lazy; use regex::Regex; -use std::rc::Rc; -//use std::sync::Arc; -use horned_owl::model::{Build, ClassExpression, ObjectPropertyExpression, SubObjectPropertyExpression, Individual, AnonymousIndividual, DataProperty, Datatype, DataRange, Literal, RcStr}; - - -pub fn translate_object_property_expression(v : &Value) -> ObjectPropertyExpression { - match v[0].as_str() { - Some("InverseOf") => translate_inverse_of(v), - Some("ObjectInverseOf") => translate_inverse_of(v), - None => translate_named_object_property(v), - Some(_) => panic!("Incorrect Property Constructor"), - } +use serde_json::Value; +use std::sync::Arc; +use crate::ofn_2_owl::util::{ + build, default_class_filler, default_data_filler, extract_iri_str, parse_number_cardinality, + parse_string_cardinality, +}; +use horned_owl::model::{ + AnonymousIndividual, ClassExpression, DataProperty, DataRange, Datatype, Individual, Literal, + ObjectPropertyExpression, ArcStr, SubObjectPropertyExpression, +}; + +static SIMPLE_LITERAL_RE: Lazy = Lazy::new(|| Regex::new("(?s)^\"(.*)\"$").unwrap()); +static LANGUAGE_TAG_RE: Lazy = Lazy::new(|| Regex::new("(?s)^\"(.*)\"@(.*)$").unwrap()); +static DATATYPE_RE: Lazy = Lazy::new(|| Regex::new("^(?s)\"(.*)\"\\^\\^(.*)$").unwrap()); + +pub fn translate_object_property_expression(v: &Value) -> Result> { + match v[0].as_str() { + Some("InverseOf") => translate_inverse_of(v), + Some("ObjectInverseOf") => translate_inverse_of(v), + None => translate_named_object_property(v), + Some(op) => bail!("Incorrect Property Constructor: {}", op), + } } -pub fn translate_sub_object_property_expression(v : &Value) -> SubObjectPropertyExpression { +pub fn translate_sub_object_property_expression(v: &Value) -> Result> { match v { Value::Array(array) => { - let operands: Vec> = (&array[1..]) - .into_iter() - .map(|x| translate_object_property_expression(&x)) - .collect(); - SubObjectPropertyExpression::ObjectPropertyChain(operands) - }, + let operands: Vec> = array[1..] + .iter() + .map(|x| translate_object_property_expression(x)) + .collect::>()?; + Ok(SubObjectPropertyExpression::ObjectPropertyChain(operands)) + } Value::String(_s) => { - let property = translate_object_property_expression(v); - SubObjectPropertyExpression::ObjectPropertyExpression(property) - }, - _ => panic!() + let property = translate_object_property_expression(v)?; + Ok(SubObjectPropertyExpression::ObjectPropertyExpression(property)) + } + _ => bail!("Expected array or string for sub-object-property expression"), } } -pub fn translate_class_expression(v : &Value) -> ClassExpression { - match v[0].as_str() { - //Some("SomeValuesFrom") => translate_some_values_from(v), - //Some("AllValuesFrom") => translate_all_values_from(v), - //Some("HasValue") => translate_has_value(v), - //Some("MinCardinality") => translate_min_cardinality(v), - //Some("MinQualifiedCardinality") => translate_min_qualified_cardinality(v), - //Some("MaxCardinality") => translate_max_cardinality(v), - //Some("MaxQualifiedCardinality") => translate_max_qualified_cardinality(v), - //Some("ExactCardinality") => translate_exact_cardinality(v), - //Some("ExactQualifiedCardinality") => translate_exact_qualified_cardinality(v), - //Some("HasSelf") => translate_has_self(v), - //Some("IntersectionOf") => translate_intersection_of(v), - //Some("UnionOf") => translate_union_of(v), - //Some("OneOf") => translate_one_of(v), - //Some("ComplementOf") => translate_complement_of(v), - //Some("InverseOf") => property_translation::translate_inverse_of(v), - - Some("ObjectSomeValuesFrom") => translate_object_some_values_from(v), - Some("ObjectAllValuesFrom") => translate_object_all_values_from(v), - Some("ObjectHasValue") => translate_object_has_value(v), - - Some("ObjectMinCardinality") => translate_object_min_cardinality(v), - Some("ObjectMaxCardinality") => translate_object_max_cardinality(v), - Some("ObjectExactCardinality") => translate_object_exact_cardinality(v), - - Some("DataMinCardinality") => translate_data_min_cardinality(v), - Some("DataMaxCardinality") => translate_data_max_cardinality(v), - Some("DataExactCardinality") => translate_data_exact_cardinality(v), - - //TODO: deprecate these - Some("ObjectMinQualifiedCardinality") => translate_object_min_qualified_cardinality(v), - Some("ObjectMaxQualifiedCardinality") => translate_object_max_qualified_cardinality(v), - Some("ObjectExactQualifiedCardinality") => translate_object_exact_qualified_cardinality(v), - Some("DataMinQualifiedCardinality") => translate_data_min_qualified_cardinality(v), - Some("DataMaxQualifiedCardinality") => translate_data_max_qualified_cardinality(v), - Some("DataExactQualifiedCardinality") => translate_data_exact_qualified_cardinality(v), - - Some("ObjectHasSelf") => translate_object_has_self(v), - Some("ObjectIntersectionOf") => translate_object_intersection_of(v), - Some("ObjectUnionOf") => translate_object_union_of(v), - Some("ObjectOneOf") => translate_object_one_of(v), - Some("ObjectComplementOf") => translate_object_complement_of(v), - - Some("DataSomeValuesFrom") => translate_data_some_values_from(v), - Some("DataAllValuesFrom") => translate_data_all_values_from(v), - Some("DataHasValue") => translate_data_has_value(v), - - - Some(_) => panic!("Not a valid (typed) OFN S-expression"), - None => translate_named_class(v), - } -} - -pub fn translate_literal_string(s: &str) -> Literal { - - let b = Build::new(); - - let simple = Regex::new("(?s)^\"(.*)\"$").unwrap(); - let language_tag = Regex::new("(?s)^\"(.*)\"@(.*)$").unwrap(); - let datatype = Regex::new("^(?s)\"(.*)\"\\^\\^(.*)$").unwrap(); - - if language_tag.is_match(s) { - match language_tag.captures(s) { - Some(x) => Literal::Language{ literal: String::from(&x[1]), - lang: String::from(&x[2]) }, - None => panic!("Not a literal with a language tag") - } - } else if datatype.is_match(s) { - match datatype.captures(s){ - Some(x) => Literal::Datatype{literal: String::from(&x[1]), - datatype_iri: b.iri(&x[2]),}, - None => panic!("Not a literal with a datatype") - - } - } else if simple.is_match(s) { - match simple.captures(s) { - Some(x) => Literal::Simple{literal: String::from(&x[1])}, - None => panic!("Not a simple literal") - } - } else { - panic!() +pub fn translate_class_expression(v: &Value) -> Result> { + match v[0].as_str() { + Some("ObjectSomeValuesFrom") => translate_object_some_values_from(v), + Some("ObjectAllValuesFrom") => translate_object_all_values_from(v), + Some("ObjectHasValue") => translate_object_has_value(v), + + Some("ObjectMinCardinality") => translate_object_min_cardinality(v), + Some("ObjectMaxCardinality") => translate_object_max_cardinality(v), + Some("ObjectExactCardinality") => translate_object_exact_cardinality(v), + + Some("DataMinCardinality") => translate_data_min_cardinality(v), + Some("DataMaxCardinality") => translate_data_max_cardinality(v), + Some("DataExactCardinality") => translate_data_exact_cardinality(v), + + Some("ObjectHasSelf") => translate_object_has_self(v), + Some("ObjectIntersectionOf") => translate_object_intersection_of(v), + Some("ObjectUnionOf") => translate_object_union_of(v), + Some("ObjectOneOf") => translate_object_one_of(v), + Some("ObjectComplementOf") => translate_object_complement_of(v), + + Some("DataSomeValuesFrom") => translate_data_some_values_from(v), + Some("DataAllValuesFrom") => translate_data_all_values_from(v), + Some("DataHasValue") => translate_data_has_value(v), + + Some(op) => bail!("Not a valid class expression operator: {}", op), + None => translate_named_class(v), + } +} + +pub fn translate_literal_string(s: &str) -> Result> { + if let Some(x) = LANGUAGE_TAG_RE.captures(s) { + Ok(Literal::Language { + literal: String::from(&x[1]), + lang: String::from(&x[2]), + }) + } else if let Some(x) = DATATYPE_RE.captures(s) { + Ok(Literal::Datatype { + literal: String::from(&x[1]), + datatype_iri: build().iri(&x[2]), + }) + } else if let Some(x) = SIMPLE_LITERAL_RE.captures(s) { + Ok(Literal::Simple { + literal: String::from(&x[1]), + }) + } else { + bail!("Not a valid literal: {}", s) } } -pub fn translate_literal(v: &Value) -> Literal { +pub fn translate_literal(v: &Value) -> Result> { match v.as_str() { Some(x) => translate_literal_string(x), - None => panic!() + None => bail!("Expected a string for literal, got: {}", v), } } -pub fn translate_data_range(v : &Value) -> DataRange { - +pub fn translate_data_range(v: &Value) -> Result> { match v { Value::String(_x) => translate_datatype_as_range(v), - Value::Array(_x) => { + Value::Array(_x) => { match v[0].as_str() { - Some("Datatype") => translate_datatype_as_range(v), - Some("DataIntersectionOf") => translate_data_intersection_of(v), - Some("DataUnionOf") => translate_data_union_of(v), - Some("DataComplementOf") => translate_data_complement_of(v), - Some("DataOneOf") => translate_data_one_of(v), + Some("Datatype") => translate_datatype_as_range(v), + Some("DataIntersectionOf") => translate_data_intersection_of(v), + Some("DataUnionOf") => translate_data_union_of(v), + Some("DataComplementOf") => translate_data_complement_of(v), + Some("DataOneOf") => translate_data_one_of(v), //TODO - //Some("DatatypeRestriction") => translate_object_some_values_from(v), - Some(_) => panic!(), - None => panic!(), - } - } , - _ => panic!(), - } + //Some("DatatypeRestriction") => translate_object_some_values_from(v), + Some(op) => bail!("Not a valid data range operator: {}", op), + None => bail!("Expected a data range operator string"), + } + } + _ => bail!("Expected string or array for data range, got: {}", v), + } } -pub fn translate_data_one_of(v : &Value) -> DataRange { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| translate_literal(&x)) - .collect(); - DataRange::DataOneOf(operands) +pub fn translate_data_one_of(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for DataOneOf")?[1..] + .iter() + .map(|x| translate_literal(x)) + .collect::>()?; + Ok(DataRange::DataOneOf(operands)) } -pub fn translate_data_complement_of(v : &Value) -> DataRange { - - let argument: DataRange = translate_data_range(&v[1]); - - DataRange::DataComplementOf(Box::new(argument)) +pub fn translate_data_complement_of(v: &Value) -> Result> { + let argument: DataRange = translate_data_range(&v[1])?; + Ok(DataRange::DataComplementOf(Box::new(argument))) } -pub fn translate_data_intersection_of(v : &Value) -> DataRange { - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| translate_data_range(&x)) - .collect(); +pub fn translate_data_intersection_of(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for DataIntersectionOf")?[1..] + .iter() + .map(|x| translate_data_range(x)) + .collect::>()?; - DataRange::DataIntersectionOf(operands) + Ok(DataRange::DataIntersectionOf(operands)) } -pub fn translate_data_union_of(v : &Value) -> DataRange { - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| translate_data_range(&x)) - .collect(); +pub fn translate_data_union_of(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for DataUnionOf")?[1..] + .iter() + .map(|x| translate_data_range(x)) + .collect::>()?; - DataRange::DataUnionOf(operands) + Ok(DataRange::DataUnionOf(operands)) } -pub fn translate_datatype(v : &Value) -> Datatype { - - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - b.datatype(iri.clone()).into() +pub fn translate_datatype(v: &Value) -> Result> { + Ok(build().datatype(extract_iri_str(v)?).into()) } -pub fn translate_datatype_as_range(v : &Value) -> DataRange { - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - DataRange::Datatype(b.datatype(iri.clone())) +pub fn translate_datatype_as_range(v: &Value) -> Result> { + Ok(DataRange::Datatype(build().datatype(extract_iri_str(v)?))) } -pub fn translate_named_object_property(v : &Value) -> ObjectPropertyExpression { - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - b.object_property(iri.clone()).into() +pub fn translate_named_object_property(v: &Value) -> Result> { + Ok(build().object_property(extract_iri_str(v)?).into()) } -pub fn translate_data_property(v : &Value) -> DataProperty { - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - b.data_property(iri.clone()).into() +pub fn translate_data_property(v: &Value) -> Result> { + Ok(build().data_property(extract_iri_str(v)?).into()) } -pub fn translate_inverse_of(v : &Value) -> ObjectPropertyExpression { - let b = Build::new(); - - let ofn_argument = v[1].clone(); - let iri = match ofn_argument { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - let argument = b.object_property(iri).into(); - ObjectPropertyExpression::InverseObjectProperty{0: argument} +pub fn translate_inverse_of(v: &Value) -> Result> { + let argument = build().object_property(extract_iri_str(&v[1])?).into(); + Ok(ObjectPropertyExpression::InverseObjectProperty { 0: argument }) } -pub fn translate_named_class(v : &Value) -> ClassExpression { - - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - b.class(iri.clone()).into() +pub fn translate_named_class(v: &Value) -> Result> { + Ok(build().class(extract_iri_str(v)?).into()) } -pub fn translate_anonymous_individual(v : &Value) -> AnonymousIndividual { - - let name = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - let s = name.as_str(); - let rc : Rc = Rc::from(s); - AnonymousIndividual{0: rc} - - //let arc : Arc = Arc::from(s); - //AnonymousIndividual{0: arc} +pub fn translate_anonymous_individual(v: &Value) -> Result> { + let rc: Arc = Arc::from(extract_iri_str(v)?); + Ok(AnonymousIndividual { 0: rc }) } -pub fn translate_individual(v : &Value) -> Individual { - +pub fn translate_individual(v: &Value) -> Result> { //TODO: handle anonymous individuals - - let b = Build::new(); - - let iri = match v { - Value::String(x) => x, - _ => panic!("Not a named entity"), - }; - - b.named_individual(iri.clone()).into() + Ok(build().named_individual(extract_iri_str(v)?).into()) } -pub fn translate_object_some_values_from(v : &Value) -> ClassExpression { +pub fn translate_object_some_values_from(v: &Value) -> Result> { + let property = translate_object_property_expression(&v[1])?; + let filler = translate_class_expression(&v[2])?; - let property = translate_object_property_expression(&v[1]); - let filler: ClassExpression = translate_class_expression(&v[2]); - - ClassExpression::ObjectSomeValuesFrom { + Ok(ClassExpression::ObjectSomeValuesFrom { ope: property, - bce : Box::new(filler) - } + bce: Box::new(filler), + }) } -pub fn translate_object_all_values_from(v : &Value) -> ClassExpression { - - let property = translate_object_property_expression(&v[1]); - let filler: ClassExpression = translate_class_expression(&v[2]); +pub fn translate_object_all_values_from(v: &Value) -> Result> { + let property = translate_object_property_expression(&v[1])?; + let filler = translate_class_expression(&v[2])?; - ClassExpression::ObjectAllValuesFrom { + Ok(ClassExpression::ObjectAllValuesFrom { ope: property, - bce : Box::new(filler) - } + bce: Box::new(filler), + }) } -pub fn translate_object_has_value(v : &Value) -> ClassExpression { +pub fn translate_object_has_value(v: &Value) -> Result> { + let property = translate_object_property_expression(&v[1])?; + let individual = translate_individual(&v[2])?; - let property = translate_object_property_expression(&v[1]); - let individual: Individual = translate_individual(&v[2]); - - ClassExpression::ObjectHasValue { + Ok(ClassExpression::ObjectHasValue { ope: property, - i : individual, - } + i: individual, + }) } -pub fn translate_object_min_cardinality(v : &Value) -> ClassExpression { - let b = Build::new(); - - let cardinality = match v[1].clone() { - Value::String(x) => { - let num: i32 = x.parse().unwrap(); - num - }, - _ => panic!("Not a named entity"), - }; +pub fn translate_object_min_cardinality(v: &Value) -> Result> { + let cardinality = parse_string_cardinality(&v[1])?; + let property = translate_object_property_expression(&v[2])?; - let property = translate_object_property_expression(&v[2]); - - let ofn = v.as_array().unwrap(); + let ofn = v.as_array().context("Expected array for ObjectMinCardinality")?; let is_qualified = ofn.len() == 4; - - let filler = - if is_qualified { - translate_class_expression(&v[3]) + + let filler = if is_qualified { + translate_class_expression(&v[3])? } else { - b.class("http://www.w3.org/2002/07/owl#Thing").into() + default_class_filler() }; - //let filler: ClassExpression = b.class("http://www.w3.org/2002/07/owl#Thing").into(); - - ClassExpression::ObjectMinCardinality { - n : cardinality as u32, - ope: property, - bce : Box::new(filler) - } -} - -pub fn translate_object_min_qualified_cardinality(v : &Value) -> ClassExpression { - - let cardinality = match v[1].clone() { - Value::String(x) => { - let num: i32 = x.parse().unwrap(); - num - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_object_property_expression(&v[2]); - let filler: ClassExpression = translate_class_expression(&v[3]); - - ClassExpression::ObjectMinCardinality { - n : cardinality as u32, + Ok(ClassExpression::ObjectMinCardinality { + n: cardinality as u32, ope: property, - bce : Box::new(filler) - } + bce: Box::new(filler), + }) } -pub fn translate_object_max_cardinality(v : &Value) -> ClassExpression { - - let b = Build::new(); +pub fn translate_object_max_cardinality(v: &Value) -> Result> { + let cardinality = parse_string_cardinality(&v[1])?; + let property = translate_object_property_expression(&v[2])?; - let cardinality = match v[1].clone() { - Value::String(x) => { - let num: i32 = x.parse().unwrap(); - num - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_object_property_expression(&v[2]); - - let ofn = v.as_array().unwrap(); + let ofn = v.as_array().context("Expected array for ObjectMaxCardinality")?; let is_qualified = ofn.len() == 4; - - let filler = - if is_qualified { - translate_class_expression(&v[3]) + + let filler = if is_qualified { + translate_class_expression(&v[3])? } else { - b.class("http://www.w3.org/2002/07/owl#Thing").into() + default_class_filler() }; - //let filler: ClassExpression = b.class("http://www.w3.org/2002/07/owl#Thing").into(); - - ClassExpression::ObjectMaxCardinality { - n : cardinality as u32, + Ok(ClassExpression::ObjectMaxCardinality { + n: cardinality as u32, ope: property, - bce : Box::new(filler) - } + bce: Box::new(filler), + }) } -pub fn translate_object_max_qualified_cardinality(v : &Value) -> ClassExpression { +pub fn translate_object_exact_cardinality(v: &Value) -> Result> { + let cardinality = parse_string_cardinality(&v[1])?; + let property = translate_object_property_expression(&v[2])?; - let cardinality = match v[1].clone() { - Value::String(x) => { - let num: i32 = x.parse().unwrap(); - num - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_object_property_expression(&v[2]); - let filler: ClassExpression = translate_class_expression(&v[3]); - - ClassExpression::ObjectMaxCardinality { - n : cardinality as u32, - ope: property, - bce : Box::new(filler) - } -} - - -pub fn translate_object_exact_cardinality(v : &Value) -> ClassExpression { - - let b = Build::new(); - - let cardinality = match v[1].clone() { - Value::String(x) => { - let num: i32 = x.parse().unwrap(); - num - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_object_property_expression(&v[2]); - - let ofn = v.as_array().unwrap(); + let ofn = v.as_array().context("Expected array for ObjectExactCardinality")?; let is_qualified = ofn.len() == 4; - - let filler = - if is_qualified { - translate_class_expression(&v[3]) + + let filler = if is_qualified { + translate_class_expression(&v[3])? } else { - b.class("http://www.w3.org/2002/07/owl#Thing").into() + default_class_filler() }; - //let filler: ClassExpression = b.class("http://www.w3.org/2002/07/owl#Thing").into(); - - ClassExpression::ObjectExactCardinality { - n : cardinality as u32, + Ok(ClassExpression::ObjectExactCardinality { + n: cardinality as u32, ope: property, - bce : Box::new(filler) - } + bce: Box::new(filler), + }) } -pub fn translate_object_exact_qualified_cardinality(v : &Value) -> ClassExpression { - - let cardinality = match v[1].clone() { - Value::String(x) => { - let num: i32 = x.parse().unwrap(); - num - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_object_property_expression(&v[2]); - let filler: ClassExpression = translate_class_expression(&v[3]); - - ClassExpression::ObjectExactCardinality { - n : cardinality as u32, - ope: property, - bce : Box::new(filler) - } +pub fn translate_object_has_self(v: &Value) -> Result> { + let property = translate_object_property_expression(&v[1])?; + Ok(ClassExpression::ObjectHasSelf(property)) } -pub fn translate_object_has_self(v : &Value) -> ClassExpression { +pub fn translate_object_intersection_of(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for ObjectIntersectionOf")?[1..] + .iter() + .map(|x| translate_class_expression(x)) + .collect::>()?; - let property = translate_object_property_expression(&v[1]); - ClassExpression::ObjectHasSelf(property) + Ok(ClassExpression::ObjectIntersectionOf(operands)) } -pub fn translate_object_intersection_of(v : &Value) -> ClassExpression { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| translate_class_expression(&x)) - .collect(); +pub fn translate_object_union_of(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for ObjectUnionOf")?[1..] + .iter() + .map(|x| translate_class_expression(x)) + .collect::>()?; - ClassExpression::ObjectIntersectionOf(operands) + Ok(ClassExpression::ObjectUnionOf(operands)) } -pub fn translate_object_union_of(v : &Value) -> ClassExpression { +pub fn translate_object_one_of(v: &Value) -> Result> { + let operands: Vec> = v.as_array() + .context("Expected array for ObjectOneOf")?[1..] + .iter() + .map(|x| translate_individual(x)) + .collect::>()?; - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| translate_class_expression(&x)) - .collect(); - - ClassExpression::ObjectUnionOf(operands) + Ok(ClassExpression::ObjectOneOf(operands)) } -pub fn translate_object_one_of(v : &Value) -> ClassExpression { - - let operands: Vec> = (&(v.as_array().unwrap())[1..]) - .into_iter() - .map(|x| translate_individual(&x)) - .collect(); - - ClassExpression::ObjectOneOf(operands) -} - -pub fn translate_object_complement_of(v : &Value) -> ClassExpression { - - let argument: ClassExpression = translate_class_expression(&v[1]); - - ClassExpression::ObjectComplementOf(Box::new(argument)) +pub fn translate_object_complement_of(v: &Value) -> Result> { + let argument = translate_class_expression(&v[1])?; + Ok(ClassExpression::ObjectComplementOf(Box::new(argument))) } -pub fn translate_data_some_values_from(v : &Value) -> ClassExpression { - - let property = translate_data_property(&v[1]); - let filler: DataRange = translate_data_range(&v[2]); +pub fn translate_data_some_values_from(v: &Value) -> Result> { + let property = translate_data_property(&v[1])?; + let filler = translate_data_range(&v[2])?; - ClassExpression::DataSomeValuesFrom { + Ok(ClassExpression::DataSomeValuesFrom { dp: property, - dr : filler - } + dr: filler, + }) } -pub fn translate_data_all_values_from(v : &Value) -> ClassExpression { +pub fn translate_data_all_values_from(v: &Value) -> Result> { + let property = translate_data_property(&v[1])?; + let filler = translate_data_range(&v[2])?; - let property = translate_data_property(&v[1]); - let filler: DataRange = translate_data_range(&v[2]); - - ClassExpression::DataAllValuesFrom { + Ok(ClassExpression::DataAllValuesFrom { dp: property, - dr : filler - } + dr: filler, + }) } -pub fn translate_data_has_value(v : &Value) -> ClassExpression { - - let property = translate_data_property(&v[1]); - let filler: Literal = translate_literal(&v[2]); +pub fn translate_data_has_value(v: &Value) -> Result> { + let property = translate_data_property(&v[1])?; + let filler = translate_literal(&v[2])?; - ClassExpression::DataHasValue { + Ok(ClassExpression::DataHasValue { dp: property, - l: filler - } + l: filler, + }) } -pub fn translate_data_min_cardinality(v : &Value) -> ClassExpression { +pub fn translate_data_min_cardinality(v: &Value) -> Result> { + let cardinality = parse_number_cardinality(&v[1])?; + let property = translate_data_property(&v[2])?; - let b = Build::new(); - - let cardinality = match v[1].clone() { - Value::Number(x) => { - match x.as_u64() { - Some(y) => y, - _ => panic!("Not a valid cardinality"), - } - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_data_property(&v[2]); - - let ofn = v.as_array().unwrap(); + let ofn = v.as_array().context("Expected array for DataMinCardinality")?; let is_qualified = ofn.len() == 4; - - let filler = - if is_qualified { - translate_data_range(&v[3]) + + let filler = if is_qualified { + translate_data_range(&v[3])? } else { - DataRange::Datatype(b.datatype("rdfs:Literal")) + default_data_filler() }; - - //let filler: DataRange = DataRange::Datatype(b.datatype("rdfs:Literal")); - - ClassExpression::DataMinCardinality { - n : cardinality as u32, + Ok(ClassExpression::DataMinCardinality { + n: cardinality as u32, dp: property, - dr : filler - } + dr: filler, + }) } -pub fn translate_data_min_qualified_cardinality(v : &Value) -> ClassExpression { +pub fn translate_data_max_cardinality(v: &Value) -> Result> { + let cardinality = parse_number_cardinality(&v[1])?; + let property = translate_data_property(&v[2])?; - let cardinality = match v[1].clone() { - Value::Number(x) => { - match x.as_u64() { - Some(y) => y, - _ => panic!("Not a valid cardinality"), - } - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_data_property(&v[2]); - let filler: DataRange = translate_data_range(&v[3]); - - ClassExpression::DataMinCardinality { - n : cardinality as u32, - dp: property, - dr : filler - } -} - -pub fn translate_data_max_cardinality(v : &Value) -> ClassExpression { - - let b = Build::new(); - - let cardinality = match v[1].clone() { - Value::Number(x) => { - match x.as_u64() { - Some(y) => y, - _ => panic!("Not a valid cardinality"), - } - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_data_property(&v[2]); - //let filler: DataRange = DataRange::Datatype(b.datatype("rdfs:Literal")); - - let ofn = v.as_array().unwrap(); + let ofn = v.as_array().context("Expected array for DataMaxCardinality")?; let is_qualified = ofn.len() == 4; - - let filler = - if is_qualified { - translate_data_range(&v[3]) + + let filler = if is_qualified { + translate_data_range(&v[3])? } else { - DataRange::Datatype(b.datatype("rdfs:Literal")) + default_data_filler() }; - ClassExpression::DataMaxCardinality { - n : cardinality as u32, - dp: property, - dr : filler - } -} - -pub fn translate_data_max_qualified_cardinality(v : &Value) -> ClassExpression { - - let cardinality = match v[1].clone() { - Value::Number(x) => { - match x.as_u64() { - Some(y) => y, - _ => panic!("Not a valid cardinality"), - } - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_data_property(&v[2]); - let filler: DataRange = translate_data_range(&v[3]); - - ClassExpression::DataMaxCardinality { - n : cardinality as u32, + Ok(ClassExpression::DataMaxCardinality { + n: cardinality as u32, dp: property, - dr : filler - } + dr: filler, + }) } -pub fn translate_data_exact_cardinality(v : &Value) -> ClassExpression { - - let b = Build::new(); - - let cardinality = match v[1].clone() { - Value::Number(x) => { - match x.as_u64() { - Some(y) => y, - _ => panic!("Not a valid cardinality"), - } - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_data_property(&v[2]); +pub fn translate_data_exact_cardinality(v: &Value) -> Result> { + let cardinality = parse_number_cardinality(&v[1])?; + let property = translate_data_property(&v[2])?; - let ofn = v.as_array().unwrap(); + let ofn = v.as_array().context("Expected array for DataExactCardinality")?; let is_qualified = ofn.len() == 4; - //let filler: DataRange = DataRange::Datatype(b.datatype("rdfs:Literal")); - let filler = - if is_qualified { - translate_data_range(&v[3]) + let filler = if is_qualified { + translate_data_range(&v[3])? } else { - DataRange::Datatype(b.datatype("rdfs:Literal")) + default_data_filler() }; - ClassExpression::DataExactCardinality { - n : cardinality as u32, - dp: property, - dr : filler - } -} - -pub fn translate_data_exact_qualified_cardinality(v : &Value) -> ClassExpression { - - let cardinality = match v[1].clone() { - Value::Number(x) => { - match x.as_u64() { - Some(y) => y, - _ => panic!("Not a valid cardinality"), - } - }, - _ => panic!("Not a named entity"), - }; - - let property = translate_data_property(&v[2]); - let filler: DataRange = translate_data_range(&v[3]); - - ClassExpression::DataExactCardinality { - n : cardinality as u32, + Ok(ClassExpression::DataExactCardinality { + n: cardinality as u32, dp: property, - dr : filler - } + dr: filler, + }) } diff --git a/src/ofn_2_owl/mod.rs b/src/ofn_2_owl/mod.rs index 18f4989..3fc8536 100644 --- a/src/ofn_2_owl/mod.rs +++ b/src/ofn_2_owl/mod.rs @@ -1,5 +1,5 @@ -pub mod expression_transducer; -pub mod axiom_transducer; pub mod annotation_transducer; +pub mod axiom_transducer; +pub mod expression_transducer; pub mod transducer; pub mod util; diff --git a/src/ofn_2_owl/transducer.rs b/src/ofn_2_owl/transducer.rs index 47cace4..b919818 100644 --- a/src/ofn_2_owl/transducer.rs +++ b/src/ofn_2_owl/transducer.rs @@ -1,10 +1,10 @@ -use serde_json::{Value}; -use crate::ofn_2_owl::axiom_transducer as axiom_transducer; -use crate::ofn_2_owl::annotation_transducer as annotation_transducer; -use horned_owl::model::{AnnotatedAxiom, RcStr}; +use anyhow::{Context, Result}; +use crate::ofn_2_owl::annotation_transducer; +use crate::ofn_2_owl::axiom_transducer; +use horned_owl::model::{AnnotatedComponent, ArcStr}; +use serde_json::Value; use std::collections::BTreeSet; - /// Given an OFN S-expression encoding an OWL axiom, /// return it's representation in Horned OWL /// @@ -14,79 +14,53 @@ use std::collections::BTreeSet; /// let ofn = json!(ofn); /// let axiom = ofn_2_owl::transducer::translate(&ofn); /// -/// println!("{:?}", axiom); - -pub fn translate(ofn : &Value) -> AnnotatedAxiom { +/// println!("{:?}", axiom); +pub fn translate(ofn: &Value) -> Result> { //split logic from annotation - let owl = get_owl(ofn); - let annotations = get_annotations(ofn); + let owl = get_owl(ofn)?; + let annotations = get_annotations(ofn)?; //translate logical component - let axiom = axiom_transducer::translate_axiom(&owl); + let axiom = axiom_transducer::translate_axiom(&owl)?; //translate annotation component let mut annotation_set = BTreeSet::new(); for annotation in annotations { - let ann = annotation_transducer::translate_annotation(&annotation); + let ann = annotation_transducer::translate_annotation(&annotation)?; annotation_set.insert(ann); } //merge logical and annotation component - let annotated_axiom = AnnotatedAxiom{axiom : axiom, - ann: annotation_set}; + let annotated_axiom = AnnotatedComponent { + component: axiom, + ann: annotation_set, + }; - annotated_axiom + Ok(annotated_axiom) } - - //TODO: reuse wiring (ofn2ldtab/annotation_translation) -pub fn get_owl(ofn : &Value) -> Value { - - let mut res = Vec::new(); - let original = &ofn.as_array().unwrap()[0..]; - for element in original { - if !is_annotation(element){ - res.push(element.clone()); - } - } - Value::Array(res) +pub fn get_owl(ofn: &Value) -> Result { + let res: Vec = ofn + .as_array() + .context("Expected OFN S-expression to be a JSON array")? + .iter() + .filter(|e| !is_annotation(e)) + .cloned() + .collect(); + Ok(Value::Array(res)) } -pub fn is_annotation(v : &Value) -> bool { - match v.clone() { - Value::Array(x) => { - match x[0].as_str(){ - Some("Annotation") => true, - Some(_) => false, - None => false, - } - } - _ => false, - } -} - -pub fn has_annotation(v : &Value) -> bool { - match v.clone() { - Value::Array(x) => is_annotation(&x[1]), //look into second argument - _ => false, - } +pub fn is_annotation(v: &Value) -> bool { + matches!(v, Value::Array(x) if x[0].as_str() == Some("Annotation")) } -pub fn get_annotations(ofn : &Value) -> Vec { - - if has_annotation(&ofn) { - - let mut res = Vec::new(); - let candidates = &ofn.as_array().unwrap()[0..]; - for candidate in candidates { - if is_annotation(candidate){ - res.push(candidate.clone()); - } - } - res - } else { - Vec::new()//empty vector - } +pub fn get_annotations(ofn: &Value) -> Result> { + Ok(ofn.as_array() + .context("Expected OFN S-expression to be a JSON array")? + .iter() + .filter(|e| is_annotation(e)) + .cloned() + .collect()) } diff --git a/src/ofn_2_owl/util.rs b/src/ofn_2_owl/util.rs index b8ffe04..d6f8a3e 100644 --- a/src/ofn_2_owl/util.rs +++ b/src/ofn_2_owl/util.rs @@ -1,30 +1,58 @@ -use serde_json::{Value}; +use anyhow::{Context, Result}; +use once_cell::sync::Lazy; use regex::Regex; +use serde_json::Value; + +use horned_owl::model::{Build, ClassExpression, DataRange, ArcStr}; + +static ANONYMOUS_RE: Lazy = Lazy::new(|| Regex::new("^(.*)_:(.+)$").unwrap()); +static LITERAL_RE: Lazy = Lazy::new(|| Regex::new("(?s)^\"(.*)\"(.*)$").unwrap()); pub fn is_literal(v: &Value) -> bool { match v { Value::String(x) => is_literal_string(x), - _ => false + _ => false, } } -pub fn is_anonynous_individual(v : &Value) -> bool { +pub fn is_anonynous_individual(v: &Value) -> bool { match v { Value::String(x) => is_literal_string(x), _ => false, } } -pub fn is_anonymous_individual(s : &str) -> bool { - let anonymous = Regex::new("^(.*)_:(.+)$").unwrap(); - anonymous.is_match(s) +pub fn is_anonymous_individual(s: &str) -> bool { + ANONYMOUS_RE.is_match(s) } -pub fn is_literal_string(s : &str) -> bool { +pub fn is_literal_string(s: &str) -> bool { + LITERAL_RE.is_match(s) +} - //NB: "(?s)" sets a flag so that . matches \n - //let literal = Regex::new("^\"(.*)\"(.*)$").unwrap(); - let literal = Regex::new("(?s)^\"(.*)\"(.*)$").unwrap(); +//TODO: check that the string is a valid IRI +pub fn extract_iri_str(v: &Value) -> Result<&str> { + v.as_str().context("Expected an IRI string") +} + +pub fn build() -> Build { + Build::new() +} + +pub fn default_class_filler() -> ClassExpression { + build().class("http://www.w3.org/2002/07/owl#Thing").into() +} + +pub fn default_data_filler() -> DataRange { + DataRange::Datatype(build().datatype("rdfs:Literal")) +} + +pub fn parse_string_cardinality(v: &Value) -> Result { + let s = v.as_str().context("Expected a string for cardinality")?; + s.parse::().context("Expected a valid cardinality number") +} - literal.is_match(s) +pub fn parse_number_cardinality(v: &Value) -> Result { + let n = v.as_u64().context("Expected a valid cardinality number")?; + Ok(n as u32) } diff --git a/src/owl_2_ofn/annotation_transducer.rs b/src/owl_2_ofn/annotation_transducer.rs index 8a22a06..892c73c 100644 --- a/src/owl_2_ofn/annotation_transducer.rs +++ b/src/owl_2_ofn/annotation_transducer.rs @@ -1,51 +1,40 @@ -use serde_json::{Value}; -use serde_json::json; -use crate::owl_2_ofn::expression_transducer as expression_transducer; -use horned_owl::model::{Annotation, AnnotationProperty, AnnotationValue, AnnotationSubject, RcStr}; +use crate::owl_2_ofn::expression_transducer; +use crate::owl_2_ofn::util::{format_iri, ofn_list}; +use horned_owl::model::{ + Annotation, AnnotationProperty, AnnotationSubject, AnnotationValue, ArcStr, +}; +use serde_json::Value; use std::collections::BTreeSet; - -pub fn translate_annotation(annotation : &Annotation) -> Value { - let operator = Value::String(String::from("Annotation")); +pub fn translate_annotation(annotation: &Annotation) -> Value { let property = translate_annotation_property(&annotation.ap); let value = translate_annotation_value(&annotation.av); - let res = vec![operator, property, value]; - Value::Array(res) + ofn_list("Annotation", vec![property, value]) } -pub fn translate_annotation_subject(annotation_subject : &AnnotationSubject) -> Value { +pub fn translate_annotation_subject(annotation_subject: &AnnotationSubject) -> Value { match annotation_subject { - AnnotationSubject::IRI(x) => json!(x.get(0..)), - AnnotationSubject::AnonymousIndividual(x) => expression_transducer::translate_anonymous_individual(x), + AnnotationSubject::IRI(x) => format_iri(x.get(0..).unwrap()), + AnnotationSubject::AnonymousIndividual(x) => { + expression_transducer::translate_anonymous_individual(x) + } } } -pub fn translate_annotation_property(property : &AnnotationProperty) -> Value { - let a = property.0.get(0..); - json!(a) +pub fn translate_annotation_property(property: &AnnotationProperty) -> Value { + format_iri(property.0.get(0..).unwrap()) } -pub fn translate_annotation_value(value : &AnnotationValue) -> Value { +pub fn translate_annotation_value(value: &AnnotationValue) -> Value { match value { AnnotationValue::Literal(x) => expression_transducer::translate_literal(x), - AnnotationValue::IRI(x) => json!(x.get(0..)), - } -} - - -pub fn translate_annotation_set(annotation_set : &BTreeSet>) -> Vec { - let mut res = Vec::new(); - for annotation in annotation_set { - res.push(translate_annotation(annotation)); - } - res + AnnotationValue::IRI(x) => format_iri(x.get(0..).unwrap()), + AnnotationValue::AnonymousIndividual(x) => { + expression_transducer::translate_anonymous_individual(x) + } + } } -//pub fn translate_annotation_set(annotation_set : &BTreeSet) -> Value { -// let operator = Value::String(String::from("AnnotationList"));//NB: not OWL -// let mut res = vec![operator]; -// for annotation in annotation_set { -// res.push(translate_annotation(annotation)); -// } -// Value::Array(res) -//} +pub fn translate_annotation_set(annotation_set: &BTreeSet>) -> Vec { + annotation_set.iter().map(|a| translate_annotation(a)).collect() +} \ No newline at end of file diff --git a/src/owl_2_ofn/axiom_transducer.rs b/src/owl_2_ofn/axiom_transducer.rs index 0f8feba..1ef508e 100644 --- a/src/owl_2_ofn/axiom_transducer.rs +++ b/src/owl_2_ofn/axiom_transducer.rs @@ -1,9 +1,23 @@ -use serde_json::{Value}; -use serde_json::json; -use crate::owl_2_ofn::expression_transducer as expression_transducer; -use crate::owl_2_ofn::annotation_transducer as annotation_transducer; -use horned_owl::model::{Axiom, SubClassOf, ClassAssertion, DeclareClass, DeclareObjectProperty, DeclareDatatype, DeclareDataProperty, DeclareNamedIndividual, DisjointClasses, DisjointUnion, EquivalentClasses, EquivalentObjectProperties, ObjectPropertyDomain, ObjectPropertyExpression, SubObjectPropertyOf, TransitiveObjectProperty, ObjectPropertyAssertion, ReflexiveObjectProperty, IrreflexiveObjectProperty, SymmetricObjectProperty, AsymmetricObjectProperty, ObjectPropertyRange, InverseObjectProperties, FunctionalObjectProperty, InverseFunctionalObjectProperty, DisjointObjectProperties, Import, SubDataPropertyOf, EquivalentDataProperties, DisjointDataProperties, DataPropertyDomain, DataPropertyRange, FunctionalDataProperty, DatatypeDefinition, HasKey, SameIndividual, DifferentIndividuals, NegativeObjectPropertyAssertion, DataPropertyAssertion, NegativeDataPropertyAssertion, AnnotationAssertion, OntologyAnnotation, DeclareAnnotationProperty, SubAnnotationPropertyOf, AnnotationPropertyDomain, AnnotationPropertyRange, RcStr}; - +use crate::owl_2_ofn::annotation_transducer; +use crate::owl_2_ofn::expression_transducer; +use crate::owl_2_ofn::util::{format_iri, ofn_list}; +use horned_owl::model::{ + AnnotationAssertion, AnnotationPropertyDomain, AnnotationPropertyRange, + AsymmetricObjectProperty, ClassAssertion, Component, DataPropertyAssertion, DataPropertyDomain, + DataPropertyRange, DatatypeDefinition, DeclareAnnotationProperty, DeclareClass, + DeclareDataProperty, DeclareDatatype, DeclareNamedIndividual, DeclareObjectProperty, + DifferentIndividuals, DisjointClasses, DisjointDataProperties, DisjointObjectProperties, + DisjointUnion, DocIRI, EquivalentClasses, EquivalentDataProperties, EquivalentObjectProperties, + FunctionalDataProperty, FunctionalObjectProperty, HasKey, Import, + InverseFunctionalObjectProperty, InverseObjectProperties, IrreflexiveObjectProperty, + NegativeDataPropertyAssertion, NegativeObjectPropertyAssertion, ObjectPropertyAssertion, + ObjectPropertyDomain, ObjectPropertyExpression, ObjectPropertyRange, OntologyAnnotation, + OntologyID, ArcStr, ReflexiveObjectProperty, Rule, SameIndividual, SubAnnotationPropertyOf, + SubClassOf, SubDataPropertyOf, SubObjectPropertyOf, SymmetricObjectProperty, + TransitiveObjectProperty, +}; +use serde_json::json; +use serde_json::Value; ///Translates an OWL axiom into an OFN S-expression /// @@ -15,480 +29,376 @@ use horned_owl::model::{Axiom, SubClassOf, ClassAssertion, DeclareClass, Declare /// sup : sup}; /// let ofn = translate(&axiom); /// println("{}", ofn); -pub fn translate(axiom : &Axiom) -> Value { - +pub fn translate(axiom: &Component) -> Value { match axiom { - Axiom::OntologyAnnotation(x) => translate_ontology_annotation(x), - Axiom::Import(x) => translate_import(x), - - Axiom::DeclareClass(x) => translate_class_declaration(x), - Axiom::DeclareObjectProperty(x) => translate_object_property_declaration(x), - Axiom::DeclareAnnotationProperty(x) => translate_declare_annotation_property(x), - Axiom::DeclareDataProperty(x) => translate_data_property_declaration(x), - Axiom::DeclareNamedIndividual(x) => translate_named_individual_declaration(x), - Axiom::DeclareDatatype(x) => translate_datatype_declaration(x), - - Axiom::SubClassOf(x) => translate_subclass_of(x), - Axiom::EquivalentClasses(x) => translate_equivalent_classes(x), - Axiom::DisjointClasses(x) => translate_disjoint_classes(x), - Axiom::DisjointUnion(x) => translate_disjoint_union(x), - - Axiom::SubObjectPropertyOf(x) => translate_sub_object_property(x), - Axiom::EquivalentObjectProperties(x) => translate_equivalent_object_properties(x), - Axiom::DisjointObjectProperties(x) => translate_disjoint_object_properties(x), - Axiom::InverseObjectProperties(x) => translate_inverse_properties(x), - Axiom::ObjectPropertyDomain(x) => translate_object_property_domain(x), - Axiom::ObjectPropertyRange(x) => translate_object_property_range(x), - Axiom::FunctionalObjectProperty(x) => translate_functional_object_property(x), - Axiom::InverseFunctionalObjectProperty(x) => translate_inverse_functional_object_property(x), - Axiom::ReflexiveObjectProperty(x) => translate_reflexive_object_property(x), - Axiom::IrreflexiveObjectProperty(x) => translate_irreflexive_object_property(x), - Axiom::SymmetricObjectProperty(x) => translate_symmetric_object_property(x), - Axiom::AsymmetricObjectProperty(x) => translate_asymmetric_object_property(x), - Axiom::TransitiveObjectProperty(x) => translate_transitive_object_property(x), - - Axiom::SubDataPropertyOf(x) => translate_sub_data_property_of(x), - Axiom::EquivalentDataProperties(x) => translate_equivalent_data_properties(x), - Axiom::DisjointDataProperties(x) => translate_disjoint_data_properties(x), - Axiom::DataPropertyDomain(x) => translate_data_property_domain(x), - Axiom::DataPropertyRange(x) => translate_data_property_range(x), - Axiom::FunctionalDataProperty(x) => translate_functional_data_property(x), - - Axiom::DatatypeDefinition(x) => translate_datatype_definition(x), - Axiom::HasKey(x) => translate_has_key(x), - - Axiom::SameIndividual(x) => translate_same_individual(x), - Axiom::DifferentIndividuals(x) => translate_different_individuals(x), - - Axiom::ClassAssertion(x) => translate_class_assertion(x), - Axiom::ObjectPropertyAssertion(x) => translate_object_property_assertion(x), - Axiom::NegativeObjectPropertyAssertion(x) => translate_negative_object_property_assertion(x), - Axiom::DataPropertyAssertion(x) => translate_data_property_assertion(x), - Axiom::NegativeDataPropertyAssertion(x) => translate_negative_data_property_assertion(x), - - Axiom::AnnotationAssertion(x) => translate_annotation_assertion(x), - Axiom::SubAnnotationPropertyOf(x) => translate_sub_annotation_property_of(x), - Axiom::AnnotationPropertyDomain(x) => translate_annotation_property_domain(x), - Axiom::AnnotationPropertyRange(x) => translate_annotation_property_range(x), - } -} - -pub fn translate_subclass_of(axiom: &SubClassOf) -> Value { - - let operator = Value::String(String::from("SubClassOf")); + Component::OntologyAnnotation(x) => translate_ontology_annotation(x), + Component::Import(x) => translate_import(x), + + Component::DeclareClass(x) => translate_class_declaration(x), + Component::DeclareObjectProperty(x) => translate_object_property_declaration(x), + Component::DeclareAnnotationProperty(x) => translate_declare_annotation_property(x), + Component::DeclareDataProperty(x) => translate_data_property_declaration(x), + Component::DeclareNamedIndividual(x) => translate_named_individual_declaration(x), + Component::DeclareDatatype(x) => translate_datatype_declaration(x), + + Component::SubClassOf(x) => translate_subclass_of(x), + Component::EquivalentClasses(x) => translate_equivalent_classes(x), + Component::DisjointClasses(x) => translate_disjoint_classes(x), + Component::DisjointUnion(x) => translate_disjoint_union(x), + + Component::SubObjectPropertyOf(x) => translate_sub_object_property(x), + Component::EquivalentObjectProperties(x) => translate_equivalent_object_properties(x), + Component::DisjointObjectProperties(x) => translate_disjoint_object_properties(x), + Component::InverseObjectProperties(x) => translate_inverse_properties(x), + Component::ObjectPropertyDomain(x) => translate_object_property_domain(x), + Component::ObjectPropertyRange(x) => translate_object_property_range(x), + Component::FunctionalObjectProperty(x) => translate_functional_object_property(x), + Component::InverseFunctionalObjectProperty(x) => { + translate_inverse_functional_object_property(x) + } + Component::ReflexiveObjectProperty(x) => translate_reflexive_object_property(x), + Component::IrreflexiveObjectProperty(x) => translate_irreflexive_object_property(x), + Component::SymmetricObjectProperty(x) => translate_symmetric_object_property(x), + Component::AsymmetricObjectProperty(x) => translate_asymmetric_object_property(x), + Component::TransitiveObjectProperty(x) => translate_transitive_object_property(x), + + Component::SubDataPropertyOf(x) => translate_sub_data_property_of(x), + Component::EquivalentDataProperties(x) => translate_equivalent_data_properties(x), + Component::DisjointDataProperties(x) => translate_disjoint_data_properties(x), + Component::DataPropertyDomain(x) => translate_data_property_domain(x), + Component::DataPropertyRange(x) => translate_data_property_range(x), + Component::FunctionalDataProperty(x) => translate_functional_data_property(x), + + Component::DatatypeDefinition(x) => translate_datatype_definition(x), + Component::HasKey(x) => translate_has_key(x), + + Component::SameIndividual(x) => translate_same_individual(x), + Component::DifferentIndividuals(x) => translate_different_individuals(x), + + Component::ClassAssertion(x) => translate_class_assertion(x), + Component::ObjectPropertyAssertion(x) => translate_object_property_assertion(x), + Component::NegativeObjectPropertyAssertion(x) => { + translate_negative_object_property_assertion(x) + } + Component::DataPropertyAssertion(x) => translate_data_property_assertion(x), + Component::NegativeDataPropertyAssertion(x) => { + translate_negative_data_property_assertion(x) + } + + Component::AnnotationAssertion(x) => translate_annotation_assertion(x), + Component::SubAnnotationPropertyOf(x) => translate_sub_annotation_property_of(x), + Component::AnnotationPropertyDomain(x) => translate_annotation_property_domain(x), + Component::AnnotationPropertyRange(x) => translate_annotation_property_range(x), + Component::OntologyID(x) => translate_ontology_id(x), + Component::DocIRI(x) => translate_doc_iri(x), + Component::Rule(x) => translate_rule(x), + } +} + +pub fn translate_subclass_of(axiom: &SubClassOf) -> Value { let subclass = expression_transducer::translate_class_expression(&axiom.sub); let superclass = expression_transducer::translate_class_expression(&axiom.sup); - let v = vec![operator, subclass, superclass]; - Value::Array(v) + ofn_list("SubClassOf", vec![subclass, superclass]) } -pub fn translate_disjoint_union(axiom : &DisjointUnion) -> Value { - let operator = Value::String(String::from("DisjointUnion")); - let lhs = axiom.0.clone(); - let lhs = expression_transducer::translate_class(&lhs); - let arguments = axiom.1.clone(); - let mut operands : Vec = arguments.into_iter() - .map(|x| expression_transducer::translate_class_expression(&x)) - .collect(); - - operands.insert(0,lhs); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_disjoint_union(axiom: &DisjointUnion) -> Value { + let lhs = expression_transducer::translate_class(&axiom.0); + let mut operands: Vec = axiom.1.iter() + .map(|x| expression_transducer::translate_class_expression(x)) + .collect(); + operands.insert(0, lhs); + ofn_list("DisjointUnion", operands) } -pub fn translate_disjoint_classes(axiom : &DisjointClasses) -> Value { - let operator = Value::String(String::from("DisjointClasses")); - let classes = axiom.0.clone(); - let mut operands : Vec = classes.into_iter() - .map(|x| expression_transducer::translate_class_expression(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_disjoint_classes(axiom: &DisjointClasses) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_class_expression(x)) + .collect(); + ofn_list("DisjointClasses", operands) } -pub fn translate_equivalent_classes(axiom : &EquivalentClasses) -> Value { - let operator = Value::String(String::from("EquivalentClasses")); - let classes = axiom.0.clone(); - let mut operands : Vec = classes.into_iter() - .map(|x| expression_transducer::translate_class_expression(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_equivalent_classes(axiom: &EquivalentClasses) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_class_expression(x)) + .collect(); + ofn_list("EquivalentClasses", operands) } -pub fn translate_object_property_axiom(operator : &str, property : &ObjectPropertyExpression) -> Value { - - let operator = Value::String(String::from(operator)); +pub fn translate_object_property_axiom( + operator: &str, + property: &ObjectPropertyExpression, +) -> Value { let argument = expression_transducer::translate_object_property_expression(property); - - let mut res = vec![operator]; - res.push(argument); - Value::Array(res) + ofn_list(operator, vec![argument]) } -pub fn translate_reflexive_object_property(axiom : &ReflexiveObjectProperty) -> Value { - translate_object_property_axiom("ReflexiveObjectProperty", &axiom.0.clone()) +pub fn translate_reflexive_object_property(axiom: &ReflexiveObjectProperty) -> Value { + translate_object_property_axiom("ReflexiveObjectProperty", &axiom.0) } -pub fn translate_irreflexive_object_property(axiom : &IrreflexiveObjectProperty) -> Value { - translate_object_property_axiom("IrreflexiveObjectProperty", &axiom.0.clone()) +pub fn translate_irreflexive_object_property(axiom: &IrreflexiveObjectProperty) -> Value { + translate_object_property_axiom("IrreflexiveObjectProperty", &axiom.0) } -pub fn translate_symmetric_object_property(axiom : &SymmetricObjectProperty) -> Value { - translate_object_property_axiom("SymmetricObjectProperty", &axiom.0.clone()) +pub fn translate_symmetric_object_property(axiom: &SymmetricObjectProperty) -> Value { + translate_object_property_axiom("SymmetricObjectProperty", &axiom.0) } -pub fn translate_asymmetric_object_property(axiom : &AsymmetricObjectProperty) -> Value { - translate_object_property_axiom("AsymmetricObjectProperty", &axiom.0.clone()) +pub fn translate_asymmetric_object_property(axiom: &AsymmetricObjectProperty) -> Value { + translate_object_property_axiom("AsymmetricObjectProperty", &axiom.0) } -pub fn translate_transitive_object_property(axiom : &TransitiveObjectProperty) -> Value { - translate_object_property_axiom("TransitiveObjectProperty", &axiom.0.clone()) +pub fn translate_transitive_object_property(axiom: &TransitiveObjectProperty) -> Value { + translate_object_property_axiom("TransitiveObjectProperty", &axiom.0) } -pub fn translate_functional_object_property(axiom : &FunctionalObjectProperty) -> Value { - translate_object_property_axiom("FunctionalObjectProperty", &axiom.0.clone()) +pub fn translate_functional_object_property(axiom: &FunctionalObjectProperty) -> Value { + translate_object_property_axiom("FunctionalObjectProperty", &axiom.0) } -pub fn translate_inverse_functional_object_property(axiom : &InverseFunctionalObjectProperty) -> Value { - translate_object_property_axiom("InverseFunctionalObjectProperty", &axiom.0.clone()) +pub fn translate_inverse_functional_object_property( + axiom: &InverseFunctionalObjectProperty, +) -> Value { + translate_object_property_axiom("InverseFunctionalObjectProperty", &axiom.0) } -pub fn translate_object_property_domain(axiom : &ObjectPropertyDomain) -> Value { - let property = expression_transducer::translate_object_property_expression(&axiom.ope.clone()); - let domain = expression_transducer::translate_class_expression(&axiom.ce.clone()); - - let operator = Value::String(String::from("ObjectPropertyDomain")); - - let mut res = vec![operator]; - res.push(property); - res.push(domain); - Value::Array(res) +pub fn translate_object_property_domain(axiom: &ObjectPropertyDomain) -> Value { + let property = expression_transducer::translate_object_property_expression(&axiom.ope); + let domain = expression_transducer::translate_class_expression(&axiom.ce); + ofn_list("ObjectPropertyDomain", vec![property, domain]) } -pub fn translate_object_property_range(axiom : &ObjectPropertyRange) -> Value { - - let operator = Value::String(String::from("ObjectPropertyRange")); - let property = expression_transducer::translate_object_property_expression(&axiom.ope.clone()); - let domain = expression_transducer::translate_class_expression(&axiom.ce.clone()); - - let mut res = vec![operator]; - res.push(property); - res.push(domain); - Value::Array(res) +pub fn translate_object_property_range(axiom: &ObjectPropertyRange) -> Value { + let property = expression_transducer::translate_object_property_expression(&axiom.ope); + let range = expression_transducer::translate_class_expression(&axiom.ce); + ofn_list("ObjectPropertyRange", vec![property, range]) } -pub fn translate_inverse_properties(axiom : &InverseObjectProperties) -> Value { - - let operator = Value::String(String::from("InverseObjectProperties")); - let lhs = expression_transducer::translate_object_property(&axiom.0.clone()); - let rhs = expression_transducer::translate_object_property(&axiom.1.clone()); - - let mut res = vec![operator]; - res.push(lhs); - res.push(rhs); - Value::Array(res) +pub fn translate_inverse_properties(axiom: &InverseObjectProperties) -> Value { + let lhs = expression_transducer::translate_object_property(&axiom.0); + let rhs = expression_transducer::translate_object_property(&axiom.1); + ofn_list("InverseObjectProperties", vec![lhs, rhs]) } -pub fn translate_disjoint_object_properties(axiom : &DisjointObjectProperties) -> Value { - let operator = Value::String(String::from("DisjointObjectProperties")); - - let arguments = axiom.0.clone(); - let mut operands : Vec = arguments.into_iter() - .map(|x| expression_transducer::translate_object_property_expression(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_disjoint_object_properties(axiom: &DisjointObjectProperties) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_object_property_expression(x)) + .collect(); + ofn_list("DisjointObjectProperties", operands) } -pub fn translate_equivalent_object_properties(axiom : &EquivalentObjectProperties) -> Value { - let operator = Value::String(String::from("EquivalentObjectProperties")); - - let arguments = axiom.0.clone(); - let mut operands : Vec = arguments.into_iter() - .map(|x| expression_transducer::translate_object_property_expression(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_equivalent_object_properties(axiom: &EquivalentObjectProperties) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_object_property_expression(x)) + .collect(); + ofn_list("EquivalentObjectProperties", operands) } -pub fn translate_sub_object_property(axiom : &SubObjectPropertyOf) -> Value { - let operator = Value::String(String::from("SubObjectPropertyOf")); +pub fn translate_sub_object_property(axiom: &SubObjectPropertyOf) -> Value { let lhs = expression_transducer::translate_sub_object_property_expression(&axiom.sub); - - let rhs = expression_transducer::translate_object_property_expression(&axiom.sup); - - let v = vec![operator, lhs, rhs]; - Value::Array(v) + let rhs = expression_transducer::translate_object_property_expression(&axiom.sup); + ofn_list("SubObjectPropertyOf", vec![lhs, rhs]) } -pub fn wrap_declaration(v : &Value) -> Value { - - let declaration = Value::String(String::from("Declaration")); - let res = vec![declaration, v.clone()]; - Value::Array(res) +pub fn wrap_declaration(v: Value) -> Value { + ofn_list("Declaration", vec![v]) } -pub fn translate_class_declaration(axiom : &DeclareClass) -> Value { - - let operator = Value::String(String::from("Class")); - let class = expression_transducer::translate_class(&axiom.0.clone()); - - let v = vec![operator, class]; - let v = Value::Array(v); - wrap_declaration(&v) +pub fn translate_class_declaration(axiom: &DeclareClass) -> Value { + let class = expression_transducer::translate_class(&axiom.0); + wrap_declaration(ofn_list("Class", vec![class])) } -pub fn translate_object_property_declaration(axiom : &DeclareObjectProperty) -> Value { - - let operator = Value::String(String::from("ObjectProperty")); - let property = expression_transducer::translate_object_property(&axiom.0.clone()); - - let v = vec![operator, property]; - let v = Value::Array(v); - wrap_declaration(&v) +pub fn translate_object_property_declaration(axiom: &DeclareObjectProperty) -> Value { + let property = expression_transducer::translate_object_property(&axiom.0); + wrap_declaration(ofn_list("ObjectProperty", vec![property])) } -pub fn translate_data_property_declaration(axiom : &DeclareDataProperty) -> Value { - - let operator = Value::String(String::from("DataProperty")); - let property = expression_transducer::translate_data_property(&axiom.0.clone()); - - let v = vec![operator, property]; - let v = Value::Array(v); - wrap_declaration(&v) +pub fn translate_data_property_declaration(axiom: &DeclareDataProperty) -> Value { + let property = expression_transducer::translate_data_property(&axiom.0); + wrap_declaration(ofn_list("DataProperty", vec![property])) } -pub fn translate_named_individual_declaration(axiom : &DeclareNamedIndividual) -> Value { - - let operator = Value::String(String::from("NamedIndividual")); - let property = expression_transducer::translate_named_individual(&axiom.0.clone()); - - let v = vec![operator, property]; - let v = Value::Array(v); - wrap_declaration(&v) +pub fn translate_named_individual_declaration(axiom: &DeclareNamedIndividual) -> Value { + let individual = expression_transducer::translate_named_individual(&axiom.0); + wrap_declaration(ofn_list("NamedIndividual", vec![individual])) } -pub fn translate_datatype_declaration(axiom : &DeclareDatatype) -> Value { - - let operator = Value::String(String::from("Datatype")); - let property = expression_transducer::translate_datatype(&axiom.0.clone()); - - let v = vec![operator, property]; - let v = Value::Array(v); - wrap_declaration(&v) +pub fn translate_datatype_declaration(axiom: &DeclareDatatype) -> Value { + let datatype = expression_transducer::translate_datatype(&axiom.0); + wrap_declaration(ofn_list("Datatype", vec![datatype])) } -pub fn translate_import(axiom : &Import) -> Value { - let operator = Value::String(String::from("Import")); +pub fn translate_import(axiom: &Import) -> Value { let a = json!(axiom.0.get(0..)); - let v = vec![operator, a]; - Value::Array(v) + ofn_list("Import", vec![a]) } -pub fn translate_sub_data_property_of(axiom : &SubDataPropertyOf) -> Value { - - let operator = Value::String(String::from("SubDataPropertyOf")); +pub fn translate_sub_data_property_of(axiom: &SubDataPropertyOf) -> Value { let sub = expression_transducer::translate_data_property(&axiom.sub); let sup = expression_transducer::translate_data_property(&axiom.sup); - - let v = vec![operator, sub, sup]; - Value::Array(v) + ofn_list("SubDataPropertyOf", vec![sub, sup]) } -pub fn translate_equivalent_data_properties(axiom : &EquivalentDataProperties) -> Value { - - let operator = Value::String(String::from("EquivalentDataProperties")); - let arguments = axiom.0.clone(); - let mut operands : Vec = arguments.into_iter() - .map(|x| expression_transducer::translate_data_property(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_equivalent_data_properties(axiom: &EquivalentDataProperties) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_data_property(x)) + .collect(); + ofn_list("EquivalentDataProperties", operands) } -pub fn translate_disjoint_data_properties(axiom : &DisjointDataProperties) -> Value { - - let operator = Value::String(String::from("DisjointDataProperties")); - let arguments = axiom.0.clone(); - let mut operands : Vec = arguments.into_iter() - .map(|x| expression_transducer::translate_data_property(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_disjoint_data_properties(axiom: &DisjointDataProperties) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_data_property(x)) + .collect(); + ofn_list("DisjointDataProperties", operands) } -pub fn translate_data_property_domain(axiom : &DataPropertyDomain) -> Value { - - let operator = Value::String(String::from("DataPropertyDomain")); +pub fn translate_data_property_domain(axiom: &DataPropertyDomain) -> Value { let property = expression_transducer::translate_data_property(&axiom.dp); let domain = expression_transducer::translate_class_expression(&axiom.ce); - - let v = vec![operator, property, domain]; - Value::Array(v) + ofn_list("DataPropertyDomain", vec![property, domain]) } -pub fn translate_data_property_range(axiom : &DataPropertyRange) -> Value { - - let operator = Value::String(String::from("DataPropertyRange")); +pub fn translate_data_property_range(axiom: &DataPropertyRange) -> Value { let property = expression_transducer::translate_data_property(&axiom.dp); let range = expression_transducer::translate_data_range(&axiom.dr); - - let v = vec![operator, property, range]; - Value::Array(v) + ofn_list("DataPropertyRange", vec![property, range]) } -pub fn translate_functional_data_property(axiom : &FunctionalDataProperty) -> Value { - - let operator = Value::String(String::from("FunctionalDataProperty")); +pub fn translate_functional_data_property(axiom: &FunctionalDataProperty) -> Value { let property = expression_transducer::translate_data_property(&axiom.0); - - let v = vec![operator, property]; - Value::Array(v) + ofn_list("FunctionalDataProperty", vec![property]) } -pub fn translate_datatype_definition(axiom : &DatatypeDefinition) -> Value { - - let operator = Value::String(String::from("DatatypeDefinition")); +pub fn translate_datatype_definition(axiom: &DatatypeDefinition) -> Value { let datatype = expression_transducer::translate_datatype(&axiom.kind); let range = expression_transducer::translate_data_range(&axiom.range); - - let v = vec![operator, datatype, range]; - Value::Array(v) + ofn_list("DatatypeDefinition", vec![datatype, range]) } -pub fn translate_has_key(axiom : &HasKey) -> Value { - let operator = Value::String(String::from("HasKey")); +pub fn translate_has_key(axiom: &HasKey) -> Value { let ce = expression_transducer::translate_class_expression(&axiom.ce); - let properties = axiom.vpe.clone(); - let mut operands : Vec = properties.into_iter() - .map(|x| expression_transducer::translate_property_expression(&x)) - .collect(); + //NB: horned owl doesn't distinguish between object and data properties in hasKey + let operands: Vec = axiom.vpe.iter() + .map(|x| expression_transducer::translate_property_expression(x)) + .collect(); - operands.insert(0,ce); - operands.insert(0,operator); - Value::Array(operands) + ofn_list("HasKey", vec![ + ce, + Value::Array(operands), + Value::Array(Vec::new()), // empty datatype properties placeholder + ]) } -pub fn translate_same_individual(axiom : &SameIndividual) -> Value { - let operator = Value::String(String::from("SameIndividual")); - let individuals = axiom.0.clone(); - let mut operands : Vec = individuals.into_iter() - .map(|x| expression_transducer::translate_individual(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_same_individual(axiom: &SameIndividual) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_individual(x)) + .collect(); + ofn_list("SameIndividual", operands) } -pub fn translate_different_individuals(axiom : &DifferentIndividuals) -> Value { - let operator = Value::String(String::from("DifferentIndividuals")); - let individuals = axiom.0.clone(); - let mut operands : Vec = individuals.into_iter() - .map(|x| expression_transducer::translate_individual(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_different_individuals(axiom: &DifferentIndividuals) -> Value { + let operands: Vec = axiom.0.iter() + .map(|x| expression_transducer::translate_individual(x)) + .collect(); + ofn_list("DifferentIndividuals", operands) } -pub fn translate_class_assertion(axiom : &ClassAssertion) -> Value { - let operator = Value::String(String::from("ClassAssertion")); +pub fn translate_class_assertion(axiom: &ClassAssertion) -> Value { let individual = expression_transducer::translate_individual(&axiom.i); let class = expression_transducer::translate_class_expression(&axiom.ce); - - let v = vec![operator, class, individual]; - Value::Array(v) + ofn_list("ClassAssertion", vec![class, individual]) } -pub fn translate_object_property_assertion(axiom : &ObjectPropertyAssertion) -> Value { - let operator = Value::String(String::from("ObjectPropertyAssertion")); +pub fn translate_object_property_assertion(axiom: &ObjectPropertyAssertion) -> Value { let from = expression_transducer::translate_individual(&axiom.from); let to = expression_transducer::translate_individual(&axiom.to); let property = expression_transducer::translate_object_property_expression(&axiom.ope); - - let v = vec![operator, property, from, to]; - Value::Array(v) + ofn_list("ObjectPropertyAssertion", vec![property, from, to]) } -pub fn translate_negative_object_property_assertion(axiom : &NegativeObjectPropertyAssertion) -> Value { - let operator = Value::String(String::from("NegativeObjectPropertyAssertion")); +pub fn translate_negative_object_property_assertion( + axiom: &NegativeObjectPropertyAssertion, +) -> Value { let from = expression_transducer::translate_individual(&axiom.from); let to = expression_transducer::translate_individual(&axiom.to); let property = expression_transducer::translate_object_property_expression(&axiom.ope); - - let v = vec![operator, property, from, to]; - Value::Array(v) + ofn_list("NegativeObjectPropertyAssertion", vec![property, from, to]) } -pub fn translate_data_property_assertion(axiom : &DataPropertyAssertion) -> Value { - let operator = Value::String(String::from("DataPropertyAssertion")); +pub fn translate_data_property_assertion(axiom: &DataPropertyAssertion) -> Value { let from = expression_transducer::translate_individual(&axiom.from); let to = expression_transducer::translate_literal(&axiom.to); let property = expression_transducer::translate_data_property(&axiom.dp); - - let v = vec![operator, property, from, to]; - Value::Array(v) + ofn_list("DataPropertyAssertion", vec![property, from, to]) } -pub fn translate_negative_data_property_assertion(axiom : &NegativeDataPropertyAssertion) -> Value { - let operator = Value::String(String::from("NegativeDataPropertyAssertion")); +pub fn translate_negative_data_property_assertion( + axiom: &NegativeDataPropertyAssertion, +) -> Value { let from = expression_transducer::translate_individual(&axiom.from); let to = expression_transducer::translate_literal(&axiom.to); let property = expression_transducer::translate_data_property(&axiom.dp); - - let v = vec![operator, property, from, to]; - Value::Array(v) + ofn_list("NegativeDataPropertyAssertion", vec![property, from, to]) } -pub fn translate_annotation_assertion(axiom : &AnnotationAssertion) -> Value { - - let operator = Value::String(String::from("AnnotationAssertion")); +pub fn translate_annotation_assertion(axiom: &AnnotationAssertion) -> Value { let subject = annotation_transducer::translate_annotation_subject(&axiom.subject); let property = annotation_transducer::translate_annotation_property(&axiom.ann.ap); let value = annotation_transducer::translate_annotation_value(&axiom.ann.av); - - let v = vec![operator, property, subject, value]; - Value::Array(v) + ofn_list("AnnotationAssertion", vec![property, subject, value]) } -pub fn translate_ontology_annotation(axiom : &OntologyAnnotation) -> Value { - let operator = Value::String(String::from("OntologyAnnotation")); +pub fn translate_ontology_annotation(axiom: &OntologyAnnotation) -> Value { let annotation = annotation_transducer::translate_annotation(&axiom.0); - let v = vec![operator, annotation]; - Value::Array(v) + ofn_list("OntologyAnnotation", vec![annotation]) } -pub fn translate_declare_annotation_property(axiom : &DeclareAnnotationProperty) -> Value { - let operator = Value::String(String::from("AnnotationProperty")); +pub fn translate_declare_annotation_property(axiom: &DeclareAnnotationProperty) -> Value { let annotation = annotation_transducer::translate_annotation_property(&axiom.0); - let v = vec![operator, annotation]; - let v = Value::Array(v); - wrap_declaration(&v) + wrap_declaration(ofn_list("AnnotationProperty", vec![annotation])) } -pub fn translate_sub_annotation_property_of(axiom : &SubAnnotationPropertyOf) -> Value { - let operator = Value::String(String::from("SubAnnotationPropertyOf")); +pub fn translate_sub_annotation_property_of(axiom: &SubAnnotationPropertyOf) -> Value { let sub = annotation_transducer::translate_annotation_property(&axiom.sub); let sup = annotation_transducer::translate_annotation_property(&axiom.sup); - let v = vec![operator, sub, sup]; - Value::Array(v) + ofn_list("SubAnnotationPropertyOf", vec![sub, sup]) } -pub fn translate_annotation_property_domain(axiom : &AnnotationPropertyDomain) -> Value { - let operator = Value::String(String::from("AnnotationPropertyDomain")); +pub fn translate_annotation_property_domain(axiom: &AnnotationPropertyDomain) -> Value { let property = annotation_transducer::translate_annotation_property(&axiom.ap); - let iri = json!(axiom.iri.get(0..)); - let v = vec![operator, property, iri]; - Value::Array(v) + let iri = format_iri(axiom.iri.get(0..).unwrap()); + ofn_list("AnnotationPropertyDomain", vec![property, iri]) } -pub fn translate_annotation_property_range(axiom : &AnnotationPropertyRange) -> Value { - let operator = Value::String(String::from("AnnotationPropertyRange")); +pub fn translate_annotation_property_range(axiom: &AnnotationPropertyRange) -> Value { let property = annotation_transducer::translate_annotation_property(&axiom.ap); - let iri = json!(axiom.iri.get(0..)); - let v = vec![operator, property, iri]; - Value::Array(v) + let iri = format_iri(axiom.iri.get(0..).unwrap()); + ofn_list("AnnotationPropertyRange", vec![property, iri]) } +pub fn translate_doc_iri(axiom: &DocIRI) -> Value { + let iri = format_iri(axiom.0.get(0..).unwrap()); + ofn_list("DocIRI", vec![iri]) +} +pub fn translate_rule(axiom: &Rule) -> Value { + let body_operands: Vec = axiom.body.iter() + .map(|atom| expression_transducer::translate_atom(atom)) + .collect(); + let head_operands: Vec = axiom.head.iter() + .map(|atom| expression_transducer::translate_atom(atom)) + .collect(); + ofn_list("DLSafeRule", vec![ + ofn_list("Body", body_operands), + ofn_list("Head", head_operands), + ]) +} +pub fn translate_ontology_id(axiom: &OntologyID) -> Value { + let iri = format_iri(axiom.iri.as_deref().unwrap_or("unknown")); + let viri = format_iri(axiom.viri.as_deref().unwrap_or("unknown")); + ofn_list("Ontology", vec![iri, viri]) +} diff --git a/src/owl_2_ofn/expression_transducer.rs b/src/owl_2_ofn/expression_transducer.rs index 906b009..63e6c17 100644 --- a/src/owl_2_ofn/expression_transducer.rs +++ b/src/owl_2_ofn/expression_transducer.rs @@ -1,239 +1,225 @@ -use serde_json::{Value}; -use serde_json::json; -use horned_owl::model::{Class, ClassExpression, NamedIndividual, ObjectProperty, ObjectPropertyExpression, SubObjectPropertyExpression, Individual, AnonymousIndividual, DataProperty, DataRange, Datatype, Literal, FacetRestriction, Facet, PropertyExpression, RcStr}; - -pub fn translate_sub_object_property_expression(expression: &SubObjectPropertyExpression) -> Value { - match expression { - SubObjectPropertyExpression::ObjectPropertyChain(x) => { - let operator = Value::String(String::from("ObjectPropertyChain")); - let mut operands : Vec = x.into_iter() - .map(|x| translate_object_property_expression(&x)) - .collect(); - operands.insert(0,operator); - Value::Array(operands) - }, - SubObjectPropertyExpression::ObjectPropertyExpression(x) => translate_object_property_expression(&x), - } -} - -pub fn translate_property_expression(expression : &PropertyExpression) -> Value { +use horned_owl::model::{ + AnnotationProperty, AnonymousIndividual, Atom, Class, ClassExpression, DArgument, DataProperty, DataRange, + Datatype, FacetRestriction, IArgument, Individual, Literal, NamedIndividual, ObjectProperty, + ObjectPropertyExpression, PropertyExpression, ArcStr, SubObjectPropertyExpression, +}; +use horned_owl::vocab::Facet; +use serde_json::json; +use serde_json::Value; + +use crate::owl_2_ofn::util::{format_iri, ofn_list}; + +pub fn translate_sub_object_property_expression( + expression: &SubObjectPropertyExpression, +) -> Value { + match expression { + SubObjectPropertyExpression::ObjectPropertyChain(x) => { + let operands: Vec = x + .iter() + .map(|x| translate_object_property_expression(&x)) + .collect(); + ofn_list("ObjectPropertyChain", operands) + } + SubObjectPropertyExpression::ObjectPropertyExpression(x) => { + translate_object_property_expression(&x) + } + } +} + +pub fn translate_property_expression(expression: &PropertyExpression) -> Value { match expression { PropertyExpression::ObjectPropertyExpression(x) => translate_object_property_expression(&x), PropertyExpression::DataProperty(x) => translate_data_property(&x), - PropertyExpression::AnnotationProperty(_x) => json!("TODO"), //TODO - } + PropertyExpression::AnnotationProperty(x) => translate_annotation_property(&x), + } } - -pub fn translate_object_property_expression(expression: &ObjectPropertyExpression) -> Value { - - match expression { - ObjectPropertyExpression::ObjectProperty(x) => translate_object_property(&x), - ObjectPropertyExpression::InverseObjectProperty(x) => translate_inverse_object_property(&x), - } +pub fn translate_object_property_expression(expression: &ObjectPropertyExpression) -> Value { + match expression { + ObjectPropertyExpression::ObjectProperty(x) => translate_object_property(&x), + ObjectPropertyExpression::InverseObjectProperty(x) => translate_inverse_object_property(&x), + } } -pub fn translate_inverse_object_property(property: &ObjectProperty) -> Value { - - let operator = Value::String(String::from("ObjectInverseOf")); - let mut res = vec![operator]; - - let operand = Value::String(String::from(property.0.get(0..).unwrap())); - res.push(operand); +pub fn translate_inverse_object_property(property: &ObjectProperty) -> Value { + let operand = translate_object_property(&property); + ofn_list("ObjectInverseOf", vec![operand]) +} - Value::Array(res) +pub fn translate_object_property(property: &ObjectProperty) -> Value { + format_iri(property.0.get(0..).unwrap()) } -pub fn translate_object_property(property: &ObjectProperty) -> Value { - let a = property.0.get(0..); - json!(a) +pub fn translate_data_property(property: &DataProperty) -> Value { + format_iri(property.0.get(0..).unwrap()) } -pub fn translate_data_property(property: &DataProperty) -> Value { - let a = property.0.get(0..); - json!(a) +pub fn translate_annotation_property(property: &AnnotationProperty) -> Value { + format_iri(property.0.get(0..).unwrap()) } -pub fn translate_class(class : &Class) -> Value { - let a = class.0.get(0..); - json!(a) +pub fn translate_class(class: &Class) -> Value { + format_iri(class.0.get(0..).unwrap()) } //TODO: not sure this is correct -pub fn translate_anonymous_individual(a : &AnonymousIndividual) -> Value { - let an = a.0.get(0..); - json!(an) +pub fn translate_anonymous_individual(a: &AnonymousIndividual) -> Value { + format_iri(a.0.get(0..).unwrap()) } -pub fn translate_named_individual(a : &NamedIndividual) -> Value { - let an = a.0.get(0..); - json!(an) +pub fn translate_named_individual(a: &NamedIndividual) -> Value { + format_iri(a.0.get(0..).unwrap()) } -pub fn translate_individual(individual : &Individual) -> Value { - - match individual { - Individual::Anonymous(x) => translate_anonymous_individual(&x), - Individual::Named(x) => translate_named_individual(&x), - } +//TODO this is an IRI +pub fn translate_individual(individual: &Individual) -> Value { + match individual { + Individual::Anonymous(x) => translate_anonymous_individual(&x), + Individual::Named(x) => translate_named_individual(&x), + } } -pub fn translate_literal(literal : &Literal) -> Value { - match literal { - //we need to use double quotes here to mark a string as a literal - Literal::Simple{literal} => json!(format!("\"{}\"",literal)), - Literal::Language{literal, lang} => json!(format!("\"{}\"@{}", literal, lang)), - Literal::Datatype{literal, datatype_iri} => json!(format!("\"{}\"^^{}", literal, datatype_iri.get(0..).unwrap())) +pub fn translate_literal(literal: &Literal) -> Value { + match literal { + //we need to use double quotes here to mark a string as a literal + Literal::Simple { literal } => json!(format!("\"{}\"", literal)), + //Literal::Simple { literal } => json!(format!("{}", literal)), + //{ if literal.is_empty() { + // json!("") } else { + // json!(format!("\"{}\"",literal))}}, + Literal::Language { literal, lang } => json!(format!("\"{}\"@{}", literal, lang)), + Literal::Datatype { + literal, + datatype_iri, + } => { + let iri = format!("<{}>", datatype_iri.get(0..).unwrap()); + json!(format!("\"{}\"^^{}", literal, iri)) } + } } -pub fn translate_n_ary_operator(operator : &str, arguments : &Vec>) -> Value { - let mut operands : Vec = arguments.into_iter() - .map(|x| translate_class_expression(&x)) - .collect(); - let operator = Value::String(String::from(operator)); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_n_ary_operator(operator: &str, arguments: &Vec>) -> Value { + let operands: Vec = arguments + .iter() + .map(|x| translate_class_expression(&x)) + .collect(); + ofn_list(operator, operands) } -pub fn translate_object_one_of(arguments : &Vec>) -> Value { - let mut operands : Vec = arguments.into_iter() - .map(|x| translate_individual(&x)) - .collect(); - let operator = Value::String(String::from("ObjectOneOf")); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_object_one_of(arguments: &Vec>) -> Value { + let operands: Vec = arguments + .iter() + .map(|x| translate_individual(&x)) + .collect(); + ofn_list("ObjectOneOf", operands) } -pub fn translate_object_complement(argument : &Box>) -> Value { - let expression : ClassExpression = *argument.clone(); - let argument = translate_class_expression(&expression); - - let operator = Value::String(String::from("ObjectComplementOf")); - let mut res = vec![operator]; - res.push(argument); - - Value::Array(res) +pub fn translate_object_complement(argument: &Box>) -> Value { + let argument = translate_class_expression(&**argument); + ofn_list("ObjectComplementOf", vec![argument]) } -pub fn translate_object_some_values_from(property : &ObjectPropertyExpression, filler : &Box> ) -> Value { - let expression : ClassExpression = *filler.clone(); - - let operator = Value::String(String::from("ObjectSomeValuesFrom")); - let filler = translate_class_expression(&expression); +pub fn translate_object_some_values_from( + property: &ObjectPropertyExpression, + filler: &Box>, +) -> Value { + let filler = translate_class_expression(&*filler); let property = translate_object_property_expression(property); - - let mut res = vec![operator]; - res.push(property); - res.push(filler); - - Value::Array(res) + ofn_list("ObjectSomeValuesFrom", vec![property, filler]) } -pub fn translate_object_all_values_from(property : &ObjectPropertyExpression, filler : &Box> ) -> Value { - let expression : ClassExpression = *filler.clone(); - - let operator = Value::String(String::from("ObjectAllValuesFrom")); - let filler = translate_class_expression(&expression); +pub fn translate_object_all_values_from( + property: &ObjectPropertyExpression, + filler: &Box>, +) -> Value { + let filler = translate_class_expression(&*filler); let property = translate_object_property_expression(property); - - let mut res = vec![operator]; - res.push(property); - res.push(filler); - - Value::Array(res) + ofn_list("ObjectAllValuesFrom", vec![property, filler]) } -pub fn translate_object_has_value(property : &ObjectPropertyExpression, value : &Individual ) -> Value { - - let operator = Value::String(String::from("ObjectHasValue")); +pub fn translate_object_has_value( + property: &ObjectPropertyExpression, + value: &Individual, +) -> Value { let value = translate_individual(value); let property = translate_object_property_expression(property); - - let mut res = vec![operator]; - res.push(property); - res.push(value); - - Value::Array(res) + ofn_list("ObjectHasValue", vec![property, value]) } -pub fn translate_object_has_self(property : &ObjectPropertyExpression) -> Value { - - let operator = Value::String(String::from("ObjectHasSelf")); +pub fn translate_object_has_self(property: &ObjectPropertyExpression) -> Value { let property = translate_object_property_expression(property); + ofn_list("ObjectHasSelf", vec![property]) +} - let mut res = vec![operator]; - res.push(property); +fn is_owl_thing(value: &Value) -> bool { + // OWL:Thing as a string (IRI) + let owl_thing_iri = ""; - Value::Array(res) + // Check if the serde_json::Value is a string and compare it to OWL:Thing IRI + match value { + Value::String(iri) => iri == owl_thing_iri, + _ => false, + } } -pub fn translate_object_cardinality(operator : &str, cardinality : &u32, property : &ObjectPropertyExpression, filler : &Box> ) -> Value { - - let expression : ClassExpression = *filler.clone(); +pub fn translate_object_cardinality( + operator: &str, + cardinality: &u32, + property: &ObjectPropertyExpression, + filler: &Box>, +) -> Value { let operator = Value::String(String::from(operator)); let cardinality = json!(cardinality.to_string()); - let filler = translate_class_expression(&expression); + let filler = translate_class_expression(&**filler); let property = translate_object_property_expression(property); let mut res = vec![operator]; res.push(cardinality); res.push(property); - res.push(filler); - Value::Array(res) -} + if filler != Value::Null && !is_owl_thing(&filler) { + res.push(filler); + } -pub fn translate_datatype(datatype : &Datatype) -> Value { - let a = datatype.0.get(0..); - json!(a) + Value::Array(res) } -pub fn translate_data_intersection_of(arguments : &Vec>) -> Value { - let mut operands : Vec = arguments.into_iter() - .map(|x| translate_data_range(&x)) - .collect(); - let operator = Value::String(String::from("DataIntersectionOf")); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_datatype(datatype: &Datatype) -> Value { + format_iri(datatype.0.get(0..).unwrap()) } -pub fn translate_data_union_of(arguments : &Vec>) -> Value { - let mut operands : Vec = arguments.into_iter() - .map(|x| translate_data_range(&x)) - .collect(); - let operator = Value::String(String::from("DataUnionOf")); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_data_intersection_of(arguments: &Vec>) -> Value { + let operands: Vec = arguments + .iter() + .map(|x| translate_data_range(&x)) + .collect(); + ofn_list("DataIntersectionOf", operands) } -pub fn translate_data_complement_of(argument : &Box>) -> Value { - - let range : DataRange = *argument.clone(); - let argument = translate_data_range(&range); - - let operator = Value::String(String::from("DataComplementOf")); - let mut res = vec![operator]; - res.push(argument); - - Value::Array(res) +pub fn translate_data_union_of(arguments: &Vec>) -> Value { + let operands: Vec = arguments + .iter() + .map(|x| translate_data_range(&x)) + .collect(); + ofn_list("DataUnionOf", operands) } -pub fn translate_data_one_of(arguments : &Vec>) -> Value { - - let mut operands : Vec = arguments.into_iter() - .map(|x| translate_literal(&x)) - .collect(); - - let operator = Value::String(String::from("DataOneOf")); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_data_complement_of(argument: &Box>) -> Value { + let argument = translate_data_range(&*argument); + ofn_list("DataComplementOf", vec![argument]) } +pub fn translate_data_one_of(arguments: &Vec>) -> Value { + let operands: Vec = arguments + .iter() + .map(|x| translate_literal(&x)) + .collect(); + ofn_list("DataOneOf", operands) +} -pub fn translate_facet(facet : &Facet) -> Value { - match facet { +pub fn translate_facet(facet: &Facet) -> Value { + match facet { Facet::Length => json!("Length"), Facet::MinLength => json!("MinLength"), Facet::MaxLength => json!("MaxLength"), @@ -248,90 +234,79 @@ pub fn translate_facet(facet : &Facet) -> Value { } } -pub fn translate_facet_restriction(facet_restriction : &FacetRestriction) -> Value { - - let operator = Value::String(String::from("FaceetRestriction")); - - let facet = facet_restriction.f.clone(); - let facet = translate_facet(&facet); - - let literal = facet_restriction.l.clone(); - let literal = translate_literal(&literal); - - let mut res = vec![operator]; - res.push(facet); - res.push(literal); - - Value::Array(res) +pub fn translate_facet_restriction(facet_restriction: &FacetRestriction) -> Value { + let facet = translate_facet(&facet_restriction.f); + let literal = translate_literal(&facet_restriction.l); + ofn_list("FacetRestriction", vec![facet, literal]) } -pub fn translate_datatype_restriction(datatype : &Datatype, facets : &Vec>) -> Value { - let operator = Value::String(String::from("DatatypeRestriction")); - let datatype = translate_datatype(datatype); - let mut operands : Vec = facets.into_iter() - .map(|x| translate_facet_restriction(&x)) - .collect(); - - operands.insert(0,datatype); - operands.insert(0,operator); - Value::Array(operands) +pub fn translate_datatype_restriction( + datatype: &Datatype, + facets: &Vec>, +) -> Value { + let datatype = translate_datatype(datatype); + let mut operands: Vec = facets + .iter() + .map(|x| translate_facet_restriction(&x)) + .collect(); + operands.insert(0, datatype); + ofn_list("DatatypeRestriction", operands) } -pub fn translate_data_range(range : &DataRange) -> Value { - +pub fn translate_data_range(range: &DataRange) -> Value { match range { DataRange::Datatype(x) => translate_datatype(&x), - DataRange::DataIntersectionOf(x) => translate_data_intersection_of(&x), - DataRange::DataUnionOf(x) => translate_data_union_of(&x), + DataRange::DataIntersectionOf(x) => translate_data_intersection_of(&x), + DataRange::DataUnionOf(x) => translate_data_union_of(&x), DataRange::DataComplementOf(x) => translate_data_complement_of(&x), DataRange::DataOneOf(x) => translate_data_one_of(&x), - DataRange::DatatypeRestriction(datatype,facets) => translate_datatype_restriction(&datatype, &facets), + DataRange::DatatypeRestriction(datatype, facets) => { + translate_datatype_restriction(&datatype, &facets) + } } } -pub fn translate_data_some_values_from(property : &DataProperty, filler : &DataRange ) -> Value { - - - let operator = Value::String(String::from("DataSomeValuesFrom")); +pub fn translate_data_some_values_from( + property: &DataProperty, + filler: &DataRange, +) -> Value { let filler = translate_data_range(&filler); let property = translate_data_property(property); - - let mut res = vec![operator]; - res.push(property); - res.push(filler); - - Value::Array(res) + ofn_list("DataSomeValuesFrom", vec![property, filler]) } -pub fn translate_data_all_values_from(property : &DataProperty, filler : &DataRange ) -> Value { - - - let operator = Value::String(String::from("DataAllValuesFrom")); +pub fn translate_data_all_values_from( + property: &DataProperty, + filler: &DataRange, +) -> Value { let filler = translate_data_range(&filler); let property = translate_data_property(property); - - let mut res = vec![operator]; - res.push(property); - res.push(filler); - - Value::Array(res) + ofn_list("DataAllValuesFrom", vec![property, filler]) } -pub fn translate_data_has_value(property : &DataProperty, literal : &Literal ) -> Value { - - let operator = Value::String(String::from("ObjectAllValuesFrom")); +pub fn translate_data_has_value(property: &DataProperty, literal: &Literal) -> Value { let value = translate_literal(literal); let property = translate_data_property(property); + ofn_list("DataHasValue", vec![property, value]) +} - let mut res = vec![operator]; - res.push(property); - res.push(value); +fn is_rdfs_literal(value: &Value) -> bool { + // RDFS:Literal as a string (IRI) + let rdfs_literal_iri = ""; - Value::Array(res) + // Check if the serde_json::Value is a string and compare it to RDFS:Literal IRI + match value { + Value::String(iri) => iri == rdfs_literal_iri, + _ => false, + } } -pub fn translate_data_cardinality(operator : &str, cardinality : &u32, property : &DataProperty, filler : &DataRange ) -> Value { - +pub fn translate_data_cardinality( + operator: &str, + cardinality: &u32, + property: &DataProperty, + filler: &DataRange, +) -> Value { let operator = Value::String(String::from(operator)); let cardinality = json!(cardinality.to_string()); let filler = translate_data_range(filler); @@ -340,33 +315,104 @@ pub fn translate_data_cardinality(operator : &str, cardinality : &u32, property let mut res = vec![operator]; res.push(cardinality); res.push(property); - res.push(filler); - - Value::Array(res) -} - - -pub fn translate_class_expression(expression: &ClassExpression) -> Value { - - match expression { - ClassExpression::Class(x) => translate_class(&x), - ClassExpression::ObjectIntersectionOf(x) => translate_n_ary_operator("ObjectIntersectionOf", &x), - ClassExpression::ObjectUnionOf(x) => translate_n_ary_operator("ObjectUnionOf", &x), - ClassExpression::ObjectComplementOf(x) => translate_object_complement(x), - ClassExpression::ObjectOneOf(x) => translate_object_one_of(&x), - ClassExpression::ObjectSomeValuesFrom{ope,bce} => translate_object_some_values_from(&ope, bce), - ClassExpression::ObjectAllValuesFrom{ope,bce} => translate_object_all_values_from(&ope, bce), - ClassExpression::ObjectHasValue{ope,i} => translate_object_has_value(&ope, &i), - ClassExpression::ObjectHasSelf(p) => translate_object_has_self(&p), - ClassExpression::ObjectMinCardinality{n, ope,bce} => translate_object_cardinality("ObjectMinCardinality", &n, &ope, bce), - ClassExpression::ObjectMaxCardinality{n, ope,bce} => translate_object_cardinality("ObjectMaxCardinality", &n, &ope, bce), - ClassExpression::ObjectExactCardinality{n, ope,bce} => translate_object_cardinality("ObjectExactCardinality", &n, &ope, bce), - ClassExpression::DataSomeValuesFrom{dp,dr} => translate_data_some_values_from(&dp, &dr), - ClassExpression::DataAllValuesFrom{dp,dr} => translate_data_all_values_from(&dp, &dr), - ClassExpression::DataHasValue{dp,l} => translate_data_has_value(&dp, &l), - ClassExpression::DataMinCardinality{n, dp,dr} => translate_data_cardinality("DataMinCardinality", &n, &dp, &dr), - ClassExpression::DataMaxCardinality{n, dp,dr} => translate_data_cardinality("DataMaxCardinality", &n, &dp, &dr), - ClassExpression::DataExactCardinality{n, dp,dr} => translate_data_cardinality("DataExactCardinality", &n, &dp, &dr), + + if filler != Value::Null && !is_rdfs_literal(&filler) { + res.push(filler); + } + + Value::Array(res) +} + +pub fn translate_atom(atom: &Atom) -> Value { + match atom { + Atom::BuiltInAtom { pred, args } => { + let pred = Value::String(String::from(pred.get(0..).unwrap())); + let mut operands = vec![pred]; + operands.extend(args.iter().map(|darg| translate_darg(darg))); + ofn_list("BuiltInAtom", operands) + } + Atom::ClassAtom { pred, arg } => { + let expression = translate_class_expression(pred); + let arg = translate_iarg(arg); + ofn_list("ClassAtom", vec![expression, arg]) + } + Atom::DataPropertyAtom { pred, args } => { + let property = translate_data_property(pred); + let arg1 = translate_darg(&args.0); + let arg2 = translate_darg(&args.1); + ofn_list("DataPropertyAtom", vec![property, arg1, arg2]) + } + Atom::DataRangeAtom { pred, arg } => { + let range = translate_data_range(pred); + let arg = translate_darg(arg); + ofn_list("DataRangeAtom", vec![range, arg]) + } + Atom::DifferentIndividualsAtom(arg1, arg2) => { + ofn_list("DifferentIndividualsAtom", vec![translate_iarg(arg1), translate_iarg(arg2)]) + } + Atom::ObjectPropertyAtom { pred, args } => { + let property = translate_object_property_expression(pred); + let arg1 = translate_iarg(&args.0); + let arg2 = translate_iarg(&args.1); + ofn_list("ObjectPropertyAtom", vec![property, arg1, arg2]) + } + Atom::SameIndividualAtom(arg1, arg2) => { + ofn_list("SameIndividualAtom", vec![translate_iarg(arg1), translate_iarg(arg2)]) + } } } +pub fn translate_iarg(iarg: &IArgument) -> Value { + match iarg { + IArgument::Variable(x) => ofn_list("Variable", vec![Value::String(String::from(x))]), + IArgument::Individual(x) => translate_individual(x), + } +} + +pub fn translate_darg(darg: &DArgument) -> Value { + match darg { + DArgument::Variable(x) => ofn_list("Variable", vec![Value::String(String::from(x))]), + DArgument::Literal(x) => translate_literal(x), + } +} + +pub fn translate_class_expression(expression: &ClassExpression) -> Value { + match expression { + ClassExpression::Class(x) => translate_class(&x), + ClassExpression::ObjectIntersectionOf(x) => { + translate_n_ary_operator("ObjectIntersectionOf", &x) + } + ClassExpression::ObjectUnionOf(x) => translate_n_ary_operator("ObjectUnionOf", &x), + ClassExpression::ObjectComplementOf(x) => translate_object_complement(x), + ClassExpression::ObjectOneOf(x) => translate_object_one_of(&x), + ClassExpression::ObjectSomeValuesFrom { ope, bce } => { + translate_object_some_values_from(&ope, bce) + } + ClassExpression::ObjectAllValuesFrom { ope, bce } => { + translate_object_all_values_from(&ope, bce) + } + ClassExpression::ObjectHasValue { ope, i } => translate_object_has_value(&ope, &i), + ClassExpression::ObjectHasSelf(p) => translate_object_has_self(&p), + ClassExpression::ObjectMinCardinality { n, ope, bce } => { + translate_object_cardinality("ObjectMinCardinality", &n, &ope, bce) + } + ClassExpression::ObjectMaxCardinality { n, ope, bce } => { + translate_object_cardinality("ObjectMaxCardinality", &n, &ope, bce) + } + ClassExpression::ObjectExactCardinality { n, ope, bce } => { + translate_object_cardinality("ObjectExactCardinality", &n, &ope, bce) + } + ClassExpression::DataSomeValuesFrom { dp, dr } => translate_data_some_values_from(&dp, &dr), + ClassExpression::DataAllValuesFrom { dp, dr } => translate_data_all_values_from(&dp, &dr), + ClassExpression::DataHasValue { dp, l } => translate_data_has_value(&dp, &l), + ClassExpression::DataMinCardinality { n, dp, dr } => { + translate_data_cardinality("DataMinCardinality", &n, &dp, &dr) + } + ClassExpression::DataMaxCardinality { n, dp, dr } => { + translate_data_cardinality("DataMaxCardinality", &n, &dp, &dr) + } + ClassExpression::DataExactCardinality { n, dp, dr } => { + translate_data_cardinality("DataExactCardinality", &n, &dp, &dr) + } + } +} diff --git a/src/owl_2_ofn/mod.rs b/src/owl_2_ofn/mod.rs index 8129693..3fc8536 100644 --- a/src/owl_2_ofn/mod.rs +++ b/src/owl_2_ofn/mod.rs @@ -1,4 +1,5 @@ -pub mod expression_transducer; -pub mod axiom_transducer; pub mod annotation_transducer; +pub mod axiom_transducer; +pub mod expression_transducer; pub mod transducer; +pub mod util; diff --git a/src/owl_2_ofn/transducer.rs b/src/owl_2_ofn/transducer.rs index 1f2d49f..10382f1 100644 --- a/src/owl_2_ofn/transducer.rs +++ b/src/owl_2_ofn/transducer.rs @@ -1,27 +1,24 @@ -use serde_json::{Value}; -use serde_json::json; -use crate::owl_2_ofn::axiom_transducer as axiom_transducer; -use crate::owl_2_ofn::annotation_transducer as annotation_transducer; -use horned_owl::model::{AnnotatedAxiom, RcStr}; - -pub fn translate(axiom : &AnnotatedAxiom) -> Value { - - let mut logical_axiom = axiom_transducer::translate(&axiom.axiom); +use crate::owl_2_ofn::annotation_transducer; +use crate::owl_2_ofn::axiom_transducer; +use horned_owl::model::{AnnotatedComponent, ArcStr}; +use serde_json::json; +use serde_json::Value; + +pub fn translate(axiom: &AnnotatedComponent) -> Value { + let mut logical_axiom = axiom_transducer::translate(&axiom.component); let annotations = &axiom.ann; - if !annotations.is_empty() { - - let annotation_list = annotation_transducer::translate_annotation_set(&axiom.ann); + if !annotations.is_empty() { + let annotation_list = annotation_transducer::translate_annotation_set(&axiom.ann); let logical_axiom_vec = logical_axiom.as_array_mut().unwrap(); for annotation in annotation_list { - logical_axiom_vec.insert(1,annotation); - } + logical_axiom_vec.insert(1, annotation); + } json!(logical_axiom_vec) - - } else { + } else { logical_axiom } } diff --git a/src/owl_2_ofn/util.rs b/src/owl_2_ofn/util.rs new file mode 100644 index 0000000..04b6860 --- /dev/null +++ b/src/owl_2_ofn/util.rs @@ -0,0 +1,14 @@ +use serde_json::json; +use serde_json::Value; + +pub fn format_iri(iri: &str) -> Value { + json!(format!("<{}>", iri)) +} + +/// Builds an OFN S-expression array from an operator name and a list of +/// (already-translated) operand Values: `["Operator", op1, op2, ...]`. +pub fn ofn_list(operator: &str, operands: Vec) -> Value { + let mut v = vec![Value::String(String::from(operator))]; + v.extend(operands); + Value::Array(v) +} diff --git a/src/prefix/mod.rs b/src/prefix/mod.rs new file mode 100644 index 0000000..5a68507 --- /dev/null +++ b/src/prefix/mod.rs @@ -0,0 +1 @@ +pub mod prefix; diff --git a/src/prefix/prefix.rs b/src/prefix/prefix.rs new file mode 100644 index 0000000..c00cb4e --- /dev/null +++ b/src/prefix/prefix.rs @@ -0,0 +1,40 @@ +use anyhow::{Context, Result}; +use clap::ArgMatches; +use sqlx::sqlite::SqlitePoolOptions; +use std::fs::File; + +pub async fn prefix(sub_matches: &ArgMatches) -> Result<()> { + let database = sub_matches.get_one::("database").unwrap(); + let prefixes = sub_matches.get_one::("prefixes").unwrap(); + + let pool = SqlitePoolOptions::new() + .max_connections(5) + .connect(database) + .await + .context("Failed to connect to the database")?; + + // Open the TSV file + let file = File::open(prefixes)?; + + // Create a CSV reader with tab delimiter + let mut rdr = csv::ReaderBuilder::new().delimiter(b'\t').from_reader(file); + + // Iterate over the records in the TSV file + for result in rdr.records() { + let record = result?; + + // Get the base and prefix from the record + let prefix = &record[0]; + let base = &record[1]; + + // Insert into the database + sqlx::query("INSERT INTO prefix (base, prefix) VALUES ($1, $2)") + .bind(base) + .bind(prefix) + .execute(&pool) + .await + .context("Failed to insert ldtab_triples into the database")?; + } + + Ok(()) +} diff --git a/src/xml/parser.rs b/src/xml/parser.rs index c961422..99b07a8 100644 --- a/src/xml/parser.rs +++ b/src/xml/parser.rs @@ -1,14 +1,13 @@ -use rio_xml::{RdfXmlParser, RdfXmlError}; -use rio_api::parser::TriplesParser; use rio_api::model::NamedNode; +use rio_api::parser::TriplesParser; +use rio_xml::{RdfXmlError, RdfXmlParser}; use std::path::Path; -use std::io::BufReader; use std::fs::File; +use std::io::BufReader; pub fn parse(file: &str) { - - let ex_file = b" + let _ex_file = b" @@ -19,15 +18,18 @@ pub fn parse(file: &str) { let path = Path::new(file); - let rdf_type = NamedNode { iri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" }; + let rdf_type = NamedNode { + iri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + }; let mut count = 0; //RdfXmlParser::new(ex_file.as_ref(), None).parse_all(&mut |t| { - RdfXmlParser::new(BufReader::new(File::open(path).unwrap()), None).parse_all(&mut |t| { - if t.predicate == rdf_type { - count += 1; - } - Ok(()) as Result<(), RdfXmlError> - }).unwrap(); - println!("Count is {:?}", count); - + RdfXmlParser::new(BufReader::new(File::open(path).unwrap()), None) + .parse_all(&mut |t| { + if t.predicate == rdf_type { + count += 1; + } + Ok(()) as Result<(), RdfXmlError> + }) + .unwrap(); + println!("Count is {:?}", count); }