use crate::ast::{ASTPropertyShape, ASTSchema};
use crate::ir::ReifierInfo;
use crate::ir::component::IRComponent;
use crate::ir::dg::{DependencyGraph, PosNeg};
use crate::ir::error::IRError;
use crate::ir::schema::IRSchema;
use crate::ir::shape::IRShape;
use crate::ir::shape_label_idx::ShapeLabelIdx;
use crate::types::{ClosedInfo, MessageMap, Severity, Target};
use rudof_iri::IriS;
use rudof_rdf::rdf_core::term::Object;
use rudof_rdf::rdf_core::term::literal::NumericLiteral;
use rudof_rdf::rdf_core::vocabs::ShaclVocab;
use rudof_rdf::rdf_core::{BuildRDF, SHACLPath};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct IRPropertyShape {
id: Object,
path: SHACLPath,
components: Vec<IRComponent>,
targets: Vec<Target>,
property_shapes: Vec<ShapeLabelIdx>,
closed_info: ClosedInfo,
deactivated: bool,
message: Option<MessageMap>,
severity: Option<Severity>,
reifier_info: Option<ReifierInfo>,
name: MessageMap,
description: MessageMap,
order: Option<NumericLiteral>,
group: Option<Object>,
}
impl IRPropertyShape {
pub fn new(id: Object, path: SHACLPath, closed_info: ClosedInfo) -> Self {
IRPropertyShape {
id,
path,
components: Vec::new(),
targets: Vec::new(),
property_shapes: Vec::new(),
closed_info,
deactivated: false,
message: None,
severity: None,
reifier_info: None,
name: MessageMap::new(),
description: MessageMap::new(),
order: None,
group: None,
}
}
pub fn with_components(mut self, components: Vec<IRComponent>) -> Self {
self.components = components;
self
}
pub fn with_targets(mut self, targets: Vec<Target>) -> Self {
self.targets = targets;
self
}
pub fn with_property_shapes(mut self, property_shapes: Vec<ShapeLabelIdx>) -> Self {
self.property_shapes = property_shapes;
self
}
pub fn with_deactivated(mut self, deactivated: bool) -> Self {
self.deactivated = deactivated;
self
}
pub fn with_severity(mut self, severity: Option<Severity>) -> Self {
self.severity = severity;
self
}
pub fn with_reifier_info(mut self, reifier_info: Option<ReifierInfo>) -> Self {
self.reifier_info = reifier_info;
self
}
pub fn with_name(mut self, name: MessageMap) -> Self {
self.name = name;
self
}
pub fn with_description(mut self, description: MessageMap) -> Self {
self.description = description;
self
}
pub fn with_order(mut self, order: Option<NumericLiteral>) -> Self {
self.order = order;
self
}
pub fn with_group(mut self, group: Option<Object>) -> Self {
self.group = group;
self
}
pub fn with_message(mut self, message: Option<MessageMap>) -> Self {
self.message = message;
self
}
pub fn id(&self) -> &Object {
&self.id
}
pub fn closed(&self) -> bool {
self.closed_info.is_closed()
}
pub fn reifier_info(&self) -> Option<&ReifierInfo> {
self.reifier_info.as_ref()
}
pub fn allowed_properties(&self) -> HashSet<IriS> {
self.closed_info.allowed_properties().unwrap_or_default()
}
pub fn path(&self) -> &SHACLPath {
&self.path
}
pub fn deactivated(&self) -> bool {
self.deactivated
}
pub fn severity(&self) -> &Severity {
match &self.severity {
None => &Severity::Violation,
Some(severity) => severity,
}
}
pub fn components(&self) -> &Vec<IRComponent> {
&self.components
}
pub fn targets(&self) -> &Vec<Target> {
&self.targets
}
pub fn property_shapes(&self) -> &Vec<ShapeLabelIdx> {
&self.property_shapes
}
pub fn message(&self) -> Option<&MessageMap> {
self.message.as_ref()
}
}
impl IRPropertyShape {
pub fn compile(shape: &ASTPropertyShape, ast: &ASTSchema, ir: &mut IRSchema) -> Result<Self, IRError> {
let mut compiled_components = Vec::new();
for component in shape.components() {
let component = IRComponent::compile(component, ast, ir)?;
compiled_components.push(component);
}
let mut compiled_prop_shapes = Vec::new();
for prop_shape in shape.property_shapes() {
let idx = ir.register_shape(prop_shape, None, ast)?;
compiled_prop_shapes.push(idx);
}
let closed_info = shape.get_closed_info(ast)?;
let reifier_info = ReifierInfo::get_reifier_info(shape, ast, ir)?;
let compiled_prop_shape = IRPropertyShape::new(shape.id().clone(), shape.path().to_owned(), closed_info)
.with_components(compiled_components)
.with_targets(shape.targets().to_owned())
.with_property_shapes(compiled_prop_shapes)
.with_deactivated(shape.is_deactivated())
.with_severity(shape.severity().cloned())
.with_reifier_info(reifier_info)
.with_name(shape.name().to_owned())
.with_description(shape.description().to_owned())
.with_order(shape.order().cloned())
.with_group(shape.group().cloned())
.with_message(shape.message().cloned());
Ok(compiled_prop_shape)
}
}
impl IRPropertyShape {
pub fn register<RDF: BuildRDF>(
&self,
graph: &mut RDF,
shapes_map: &HashMap<ShapeLabelIdx, IRShape>,
) -> Result<(), IRError> {
let id: RDF::Subject = self.id.clone().try_into().unwrap_or_else(|_| unreachable!());
graph
.add_type(id.clone(), ShaclVocab::sh_property_shape())
.map_err(|e| IRError::from_rdf_err::<RDF>("add type", e))?;
self.name.iter_literals().try_for_each(|lit| {
graph
.add_triple::<_, _, RDF::Literal>(id.clone(), ShaclVocab::sh_name(), lit.into())
.map_err(IRError::add_triple::<RDF>)
})?;
self.description.iter_literals().try_for_each(|lit| {
graph
.add_triple::<_, _, RDF::Literal>(id.clone(), ShaclVocab::sh_description(), lit.into())
.map_err(IRError::add_triple::<RDF>)
})?;
if let Some(order) = &self.order {
let lit: RDF::Literal = match order {
NumericLiteral::Integer(i) => (*i).into(),
NumericLiteral::Byte(_) => todo!(),
NumericLiteral::Short(_) => todo!(),
NumericLiteral::NonNegativeInteger(_) => todo!(),
NumericLiteral::UnsignedLong(_) => todo!(),
NumericLiteral::UnsignedInt(_) => todo!(),
NumericLiteral::UnsignedShort(_) => todo!(),
NumericLiteral::UnsignedByte(_) => todo!(),
NumericLiteral::PositiveInteger(_) => todo!(),
NumericLiteral::NegativeInteger(_) => todo!(),
NumericLiteral::NonPositiveInteger(_) => todo!(),
NumericLiteral::Long(_) => todo!(),
NumericLiteral::Decimal(_) => todo!(),
NumericLiteral::Double(f) => (*f).into(),
NumericLiteral::Float(f) => f.to_string().into(),
};
graph
.add_triple(id.clone(), ShaclVocab::sh_order(), lit)
.map_err(IRError::add_triple::<RDF>)?;
}
if let Some(group) = &self.group {
graph
.add_triple(id.clone(), ShaclVocab::sh_group(), group.clone())
.map_err(IRError::add_triple::<RDF>)?;
}
if let SHACLPath::Predicate { pred } = &self.path {
graph
.add_triple(id.clone(), ShaclVocab::sh_path(), pred.clone())
.map_err(IRError::add_triple::<RDF>)?;
} else {
unimplemented!()
}
self.components
.iter()
.try_for_each(|component| component.register(&self.id, graph, shapes_map))?;
self.targets
.iter()
.try_for_each(|target| target.register(&self.id, graph))
.map_err(|e| IRError::from_rdf_err::<RDF>("add target to graph", e))?;
if self.deactivated {
let lit: RDF::Literal = "true".to_string().into();
graph
.add_triple(id.clone(), ShaclVocab::sh_deactivated(), lit)
.map_err(IRError::add_triple::<RDF>)?;
}
if let Some(severity) = &self.severity {
graph
.add_triple::<_, _, IriS>(id.clone(), ShaclVocab::sh_severity(), severity.clone().into())
.map_err(IRError::add_triple::<RDF>)?;
}
Ok(())
}
}
impl IRPropertyShape {
pub fn add_edges(
&self,
idx: ShapeLabelIdx,
dg: &mut DependencyGraph,
posneg: PosNeg,
ir: &IRSchema,
cache: &mut HashSet<ShapeLabelIdx>,
) {
for component in &self.components {
component.add_edges(idx, dg, posneg, ir, cache);
}
for prop_shape_idx in &self.property_shapes {
if let Some(shape) = ir.get_shape_from_idx(prop_shape_idx) {
dg.add_edge(idx, *prop_shape_idx, posneg);
if !cache.contains(prop_shape_idx) {
cache.insert(*prop_shape_idx);
shape.add_edges(*prop_shape_idx, dg, posneg, ir, cache);
}
}
}
}
}