swls_core/systems/lov/
extractor.rs1use 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}