Skip to main content

swls_core/systems/lov/
extractor.rs

1use std::collections::HashMap;
2
3use bevy_ecs::prelude::*;
4use sophia_api::{
5    prelude::{Any, Dataset},
6    quad::Quad as _,
7    term::matcher::TermMatcher,
8};
9use tracing::{debug, instrument};
10
11use crate::{
12    lsp_types::TextDocumentItem,
13    prelude::*,
14    util::{fs::Fs, ns::rdfs},
15};
16
17#[derive(Component, Debug)]
18pub struct OntologyExtract;
19
20#[instrument(skip(commands))]
21pub fn init_ontology_extractor(mut commands: Commands, fs: Res<Fs>) {
22    for local in swls_lov::LOCAL_PREFIXES
23        .iter()
24        .filter(|x| ["rdf", "rdfs", "owl"].iter().any(|y| *y == x.name))
25    {
26        let url = fs.0.lov_url(&local.location, &local.name).unwrap();
27        debug!("Virtual url {}", url.to_string());
28
29        let item = TextDocumentItem {
30            version: 1,
31            uri: url.clone(),
32            language_id: String::from("turtle"),
33            text: String::new(),
34        };
35
36        let spawn = spawn_or_insert(
37            url.clone(),
38            (
39                Source(local.content.to_string()),
40                RopeC(ropey::Rope::from_str(&local.content)),
41                Label(url),
42                Wrapped(item),
43                Types(HashMap::new()),
44            ),
45            Some("turtle".into()),
46            OntologyExtract,
47        );
48
49        debug!("Init ontology {}", local.name);
50        commands.queue(move |world: &mut World| {
51            debug!("spawned ontology entity");
52            spawn(world);
53        });
54    }
55}
56
57#[instrument(skip(query, extractor))]
58pub fn check_added_ontology_extract(
59    query: Query<(&Triples, &Label), (Added<Triples>, With<OntologyExtract>)>,
60    mut extractor: ResMut<OntologyExtractor>,
61) {
62    let mut changed = false;
63    for (triples, label) in &query {
64        debug!("Added triples from {}", label.as_str());
65        extractor.quads.extend(triples.0.iter().cloned());
66        changed = true;
67    }
68    if changed {
69        extractor.extract();
70    }
71}
72
73#[derive(Debug, Resource)]
74pub struct OntologyExtractor {
75    quads: Vec<MyQuad<'static>>,
76    properties: Vec<MyTerm<'static>>,
77    classes: Vec<MyTerm<'static>>,
78}
79
80struct LocalMatcher<'a> {
81    properties: &'a [MyTerm<'static>],
82}
83
84impl TermMatcher for LocalMatcher<'_> {
85    type Term = MyTerm<'static>;
86
87    fn matches<T2: sophia_api::prelude::Term + ?Sized>(&self, term: &T2) -> bool {
88        for p in self.properties {
89            if term.eq(p) {
90                return false;
91            }
92        }
93
94        true
95    }
96}
97
98impl OntologyExtractor {
99    pub fn new() -> Self {
100        Self {
101            quads: vec![],
102            classes: vec![MyTerm::<'static>::named_node(
103                "http://www.w3.org/2000/01/rdf-schema#Class",
104                0..1,
105            )],
106            properties: vec![MyTerm::<'static>::named_node(
107                "http://www.w3.org/1999/02/22-rdf-syntax-ns#Property",
108                0..1,
109            )],
110        }
111    }
112
113    pub fn properties<'a>(&'a self) -> &'a [MyTerm<'static>] {
114        &self.properties[..]
115    }
116
117    pub fn classes<'a>(&'a self) -> &'a [MyTerm<'static>] {
118        &self.classes[..]
119    }
120
121    fn extract_step(quads: &Vec<MyQuad<'static>>, items: &mut Vec<MyTerm<'static>>) -> bool {
122        let new_items: Vec<_> = quads
123            .quads_matching(
124                LocalMatcher { properties: &items },
125                [rdfs::subClassOf],
126                &items[..],
127                Any,
128            )
129            .flatten()
130            .map(|x| x.to_s().to_owned())
131            .collect();
132
133        let added = !new_items.is_empty();
134        items.extend(new_items);
135        added
136    }
137
138    fn extract(&mut self) {
139        loop {
140            if !OntologyExtractor::extract_step(&self.quads, &mut self.properties) {
141                break;
142            }
143        }
144
145        loop {
146            if !OntologyExtractor::extract_step(&self.quads, &mut self.classes) {
147                break;
148            }
149        }
150    }
151}