1use super::{Shacl2ShExConfig, Shacl2ShExError};
2use iri_s::IriS;
3use prefixmap::IriRef;
4use shacl_ast::{
5 component::Component, node_shape::NodeShape, property_shape::PropertyShape,
6 shape::Shape as ShaclShape, target::Target, Schema as ShaclSchema,
7};
8use shex_ast::{
9 BNode, NodeConstraint, Schema as ShExSchema, Shape as ShExShape, ShapeExpr, ShapeExprLabel,
10 TripleExpr, TripleExprWrapper, ValueSetValue,
11};
12use srdf::{Object, RDFNode, SHACLPath};
13use tracing::debug;
14
15#[allow(dead_code)] pub struct Shacl2ShEx {
17 config: Shacl2ShExConfig,
18 current_shex: ShExSchema,
19}
20
21impl Shacl2ShEx {
22 pub fn new(config: &Shacl2ShExConfig) -> Shacl2ShEx {
23 Shacl2ShEx {
24 config: config.clone(),
25 current_shex: ShExSchema::new(),
26 }
27 }
28
29 pub fn current_shex(&self) -> &ShExSchema {
30 &self.current_shex
31 }
32
33 pub fn convert(&mut self, schema: &ShaclSchema) -> Result<(), Shacl2ShExError> {
34 let prefixmap = schema.prefix_map().without_rich_qualifying();
35 self.current_shex = ShExSchema::new().with_prefixmap(Some(prefixmap));
36 for (_, shape) in schema.iter() {
37 match &shape {
38 shacl_ast::shape::Shape::NodeShape(ns) => {
39 let (label, shape_expr, is_abstract) = self.convert_shape(ns, schema)?;
40 self.current_shex.add_shape(label, shape_expr, is_abstract)
41 }
42 shacl_ast::shape::Shape::PropertyShape(_) => {
43 }
45 }
46 }
47 Ok(())
48 }
49
50 pub fn convert_shape(
51 &self,
52 shape: &NodeShape,
53 schema: &ShaclSchema,
54 ) -> Result<(ShapeExprLabel, ShapeExpr, bool), Shacl2ShExError> {
55 let label = self.rdfnode2label(shape.id())?;
56 let shape_expr = self.node_shape2shape_expr(shape, schema)?;
57 let is_abstract = false; Ok((label, shape_expr, is_abstract))
59 }
60
61 pub fn rdfnode2label(&self, node: &RDFNode) -> Result<ShapeExprLabel, Shacl2ShExError> {
62 match node {
63 srdf::Object::Iri(iri) => Ok(ShapeExprLabel::iri(iri.clone())),
64 srdf::Object::BlankNode(bn) => Ok(ShapeExprLabel::bnode(BNode::new(bn))),
65 srdf::Object::Literal(lit) => Err(Shacl2ShExError::RDFNode2LabelLiteral {
66 literal: lit.clone(),
67 }),
68 }
69 }
70
71 pub fn node_shape2shape_expr(
72 &self,
73 shape: &NodeShape,
74 schema: &ShaclSchema,
75 ) -> Result<ShapeExpr, Shacl2ShExError> {
76 let mut exprs = Vec::new();
77 for node in shape.property_shapes() {
78 match schema.get_shape(node) {
79 None => todo!(),
80 Some(shape) => match shape {
81 ShaclShape::PropertyShape(ps) => {
82 let tc = self.property_shape2triple_constraint(ps)?;
83 exprs.push(tc);
84 Ok(())
85 }
86 ShaclShape::NodeShape(ns) => Err(Shacl2ShExError::NotExpectedNodeShape {
87 node_shape: ns.clone(),
88 }),
89 },
90 }?
91 }
92 let is_closed = None; let extra = None; let mut te = if exprs.is_empty() {
95 None
96 } else {
97 Some(TripleExpr::each_of(exprs))
98 };
99 if self.config.add_target_class() {
100 let target_class_expr = self.convert_target_decls(shape.targets(), schema)?;
101 te = match (te, target_class_expr) {
102 (None, None) => None,
103 (None, Some(t)) => Some(t),
104 (Some(t), None) => Some(t),
105 (Some(t1), Some(t2)) => Some(self.merge_triple_exprs(&t1, &t2)),
106 };
107 }
108 let shape = ShExShape::new(is_closed, extra, te);
109 Ok(ShapeExpr::shape(shape))
110 }
111
112 pub fn convert_target_decls(
114 &self,
115 targets: &Vec<Target>,
116 schema: &ShaclSchema,
117 ) -> Result<Option<TripleExpr>, Shacl2ShExError> {
118 let mut values = Vec::new();
119 for target in targets {
120 if let Some(value) = self.target2value_set_value(target, schema)? {
121 values.push(value);
122 }
123 }
124 let value_cls = ShapeExpr::node_constraint(NodeConstraint::new().with_values(values));
125 let tc = TripleExpr::triple_constraint(
126 None,
127 None,
128 IriRef::iri(IriS::rdf_type()),
129 Some(value_cls),
130 None,
131 None,
132 );
133 Ok(Some(tc))
134 }
135
136 pub fn target2value_set_value(
137 &self,
138 target: &Target,
139 _schema: &ShaclSchema,
140 ) -> Result<Option<ValueSetValue>, Shacl2ShExError> {
141 match target {
142 Target::TargetNode(_) => Ok(None),
143 Target::TargetClass(cls) => {
144 let value_set_value = match cls {
145 Object::Iri(iri) => Ok(ValueSetValue::iri(IriRef::iri(iri.clone()))),
146 Object::BlankNode(bn) => {
147 Err(Shacl2ShExError::UnexpectedBlankNodeForTargetClass {
148 bnode: bn.clone(),
149 })
150 }
151 Object::Literal(lit) => Err(Shacl2ShExError::UnexpectedLiteralForTargetClass {
152 literal: lit.clone(),
153 }),
154 }?;
155 Ok(Some(value_set_value))
156 }
157 Target::TargetSubjectsOf(_) => Ok(None),
158 Target::TargetObjectsOf(_) => Ok(None),
159 Target::TargetImplicitClass(_) => Ok(None),
160 }
161 }
162
163 pub fn merge_triple_exprs(&self, te1: &TripleExpr, te2: &TripleExpr) -> TripleExpr {
164 match te1 {
165 TripleExpr::EachOf {
166 id,
167 expressions,
168 min,
169 max,
170 sem_acts,
171 annotations,
172 } => match te2 {
173 TripleExpr::EachOf {
174 id: _,
175 expressions: exprs,
176 min: _,
177 max: _,
178 sem_acts: _,
179 annotations: _,
180 } => TripleExpr::EachOf {
181 id: id.clone(),
182 expressions: Self::merge_expressions(expressions, exprs),
183 min: *min,
184 max: *max,
185 sem_acts: sem_acts.clone(),
186 annotations: annotations.clone(),
187 },
188 tc @ TripleExpr::TripleConstraint {
189 id,
190 negated: _,
191 inverse: _,
192 predicate: _,
193 value_expr: _,
194 min,
195 max,
196 sem_acts,
197 annotations,
198 } => TripleExpr::EachOf {
199 id: id.clone(),
200 expressions: Self::merge_expressions(
201 expressions,
202 &vec![TripleExprWrapper { te: tc.clone() }],
203 ),
204 min: *min,
205 max: *max,
206 sem_acts: sem_acts.clone(),
207 annotations: annotations.clone(),
208 },
209 _ => todo!(),
210 },
211 tc @ TripleExpr::TripleConstraint {
212 id,
213 negated: _,
214 inverse: _,
215 predicate: _,
216 value_expr: _,
217 min,
218 max,
219 sem_acts,
220 annotations,
221 } => match te2 {
222 TripleExpr::EachOf {
223 id: _,
224 expressions: exprs,
225 min: _,
226 max: _,
227 sem_acts: _,
228 annotations: _,
229 } => TripleExpr::EachOf {
230 id: id.clone(),
231 expressions: Self::merge_expressions(
232 &vec![TripleExprWrapper { te: tc.clone() }],
233 exprs,
234 ),
235 min: *min,
236 max: *max,
237 sem_acts: sem_acts.clone(),
238 annotations: annotations.clone(),
239 },
240 tc2 @ TripleExpr::TripleConstraint {
241 id,
242 negated: _,
243 inverse: _,
244 predicate: _,
245 value_expr: _,
246 min,
247 max,
248 sem_acts,
249 annotations,
250 } => TripleExpr::EachOf {
251 id: id.clone(),
252 expressions: vec![
253 TripleExprWrapper { te: tc.clone() },
254 TripleExprWrapper { te: tc2.clone() },
255 ],
256 min: *min,
257 max: *max,
258 sem_acts: sem_acts.clone(),
259 annotations: annotations.clone(),
260 },
261 _ => todo!(),
262 },
263 _ => todo!(),
264 }
265 }
266
267 pub fn merge_expressions(
268 e1: &Vec<TripleExprWrapper>,
269 e2: &Vec<TripleExprWrapper>,
270 ) -> Vec<TripleExprWrapper> {
271 let mut es = Vec::new();
272 for e in e1 {
273 es.push(e.clone())
274 }
275 for e in e2 {
276 es.push(e.clone())
277 }
278 es
279 }
280
281 pub fn property_shape2triple_constraint(
282 &self,
283 shape: &PropertyShape,
284 ) -> Result<TripleExpr, Shacl2ShExError> {
285 let predicate = self.shacl_path2predicate(shape.path())?;
286 let negated = None;
287 let inverse = None;
288 let se = self.components2shape_expr(shape.components())?;
289 let min = None;
290 let max = None;
291 Ok(TripleExpr::triple_constraint(
292 negated, inverse, predicate, se, min, max,
293 ))
294 }
295
296 pub fn components2shape_expr(
297 &self,
298 components: &Vec<Component>,
299 ) -> Result<Option<ShapeExpr>, Shacl2ShExError> {
300 let mut ses = Vec::new();
301 for c in components {
302 let se = self.component2shape_expr(c)?;
303 ses.push(se);
304 }
305 if ses.is_empty() {
306 Ok(None)
307 } else {
308 match ses.len() {
309 1 => {
310 let se = &ses[0];
311 Ok(Some(se.clone()))
312 }
313 _ => {
314 debug!("More than one component: {components:?}, taking only the first one");
316 let se = &ses[0];
317 Ok(Some(se.clone()))
318 }
319 }
320 }
321 }
322
323 pub fn create_class_constraint(&self, cls: &RDFNode) -> Result<ShapeExpr, Shacl2ShExError> {
324 let rdf_type = IriRef::iri(IriS::rdf_type());
325 let value = match cls {
326 Object::Iri(iri) => ValueSetValue::iri(IriRef::iri(iri.clone())),
327 Object::BlankNode(_) => todo!(),
328 Object::Literal(_) => todo!(),
329 };
330 let cls = NodeConstraint::new().with_values(vec![value]);
331 let te = TripleExpr::triple_constraint(
332 None,
333 None,
334 rdf_type,
335 Some(ShapeExpr::node_constraint(cls)),
336 None,
337 None,
338 );
339 let se = ShapeExpr::shape(ShExShape::new(None, None, Some(te)));
340 Ok(se)
341 }
342
343 pub fn component2shape_expr(
344 &self,
345 component: &Component,
346 ) -> Result<ShapeExpr, Shacl2ShExError> {
347 match component {
348 Component::Class(cls) => {
349 debug!("TODO: Converting Class components for {cls:?} doesn't match rdfs:subClassOf semantics of SHACL yet");
350 let se = self.create_class_constraint(cls)?;
351 Ok(se)
352 }
353 Component::Datatype(dt) => Ok(ShapeExpr::node_constraint(
354 NodeConstraint::new().with_datatype(dt.clone()),
355 )),
356 Component::NodeKind(_) => todo!(),
357 Component::MinCount(_) => todo!(),
358 Component::MaxCount(_) => todo!(),
359 Component::MinExclusive(_) => todo!(),
360 Component::MaxExclusive(_) => todo!(),
361 Component::MinInclusive(_) => todo!(),
362 Component::MaxInclusive(_) => todo!(),
363 Component::MinLength(_) => todo!(),
364 Component::MaxLength(_) => todo!(),
365 Component::Pattern {
366 pattern: _,
367 flags: _,
368 } => todo!(),
369 Component::UniqueLang(_) => todo!(),
370 Component::LanguageIn { langs: _ } => todo!(),
371 Component::Equals(_) => todo!(),
372 Component::Disjoint(_) => todo!(),
373 Component::LessThan(_) => todo!(),
374 Component::LessThanOrEquals(_) => todo!(),
375 Component::Or { shapes: _ } => {
376 debug!("Not implemented OR Shapes");
377 Ok(ShapeExpr::empty_shape())
378 }
379 Component::And { shapes: _ } => todo!(),
380 Component::Not { shape: _ } => todo!(),
381 Component::Xone { shapes: _ } => todo!(),
382 Component::Closed {
383 is_closed: _,
384 ignored_properties: _,
385 } => todo!(),
386 Component::Node { shape: _ } => todo!(),
387 Component::HasValue { value: _ } => todo!(),
388 Component::In { values: _ } => todo!(),
389 Component::QualifiedValueShape {
390 shape: _,
391 qualified_min_count: _,
392 qualified_max_count: _,
393 qualified_value_shapes_disjoint: _,
394 } => todo!(),
395 }
396 }
397
398 pub fn shacl_path2predicate(&self, path: &SHACLPath) -> Result<IriRef, Shacl2ShExError> {
399 match path {
400 SHACLPath::Predicate { pred } => Ok(IriRef::iri(pred.clone())),
401 _ => todo!(),
402 }
403 }
404}