use std::collections::HashMap;
use snomed_core::constants::ROLE_GROUP;
use snomed_core::sctid::SctId;
use snomed_owl::{Axiom, ClassExpression};
use crate::skipped::SkippedConstruct;
pub(crate) type Attribute = (SctId, SctId);
#[derive(Debug, Default, Clone)]
pub(crate) struct StatedProfile {
pub(crate) parents: Vec<SctId>,
pub(crate) ungrouped: Vec<Attribute>,
pub(crate) groups: Vec<Vec<Attribute>>,
}
pub(crate) fn extract_stated_profiles<'a>(
axioms: impl IntoIterator<Item = &'a Axiom>,
) -> (HashMap<SctId, StatedProfile>, Vec<SkippedConstruct>) {
let mut profiles: HashMap<SctId, StatedProfile> = HashMap::new();
let mut skipped = Vec::new();
for axiom in axioms {
match axiom {
Axiom::SubClassOf {
sub: ClassExpression::Concept(id),
sup,
} => {
add_conjuncts(*id, sup, &mut profiles, &mut skipped);
}
Axiom::SubClassOf { .. } => {}
Axiom::EquivalentClasses(ops) => {
for (i, op) in ops.iter().enumerate() {
let ClassExpression::Concept(id) = op else {
continue;
};
for (j, other) in ops.iter().enumerate() {
if i != j {
add_conjuncts(*id, other, &mut profiles, &mut skipped);
}
}
}
}
_ => {}
}
}
(profiles, skipped)
}
fn add_conjuncts(
concept: SctId,
expr: &ClassExpression,
profiles: &mut HashMap<SctId, StatedProfile>,
skipped: &mut Vec<SkippedConstruct>,
) {
match expr {
ClassExpression::ObjectIntersectionOf(ops) => {
for op in ops {
add_conjunct(concept, op, profiles, skipped);
}
}
other => add_conjunct(concept, other, profiles, skipped),
}
}
fn add_conjunct(
concept: SctId,
expr: &ClassExpression,
profiles: &mut HashMap<SctId, StatedProfile>,
skipped: &mut Vec<SkippedConstruct>,
) {
let profile = profiles.entry(concept).or_default();
match expr {
ClassExpression::Concept(parent) => profile.parents.push(*parent),
ClassExpression::ObjectSomeValuesFrom { attribute, filler } if *attribute == ROLE_GROUP => {
match extract_group_attributes(filler) {
Some(attrs) => profiles.entry(concept).or_default().groups.push(attrs),
None => skipped.push(SkippedConstruct::UnmodeledAttributeShape { concept }),
}
}
ClassExpression::ObjectSomeValuesFrom { attribute, filler } => match filler.as_ref() {
ClassExpression::Concept(value) => profiles
.entry(concept)
.or_default()
.ungrouped
.push((*attribute, *value)),
_ => skipped.push(SkippedConstruct::UnmodeledAttributeShape { concept }),
},
ClassExpression::DataHasValue { attribute, .. } => {
skipped.push(SkippedConstruct::ConcreteValue {
attribute: *attribute,
});
}
ClassExpression::ObjectIntersectionOf(_) => {
skipped.push(SkippedConstruct::UnmodeledAttributeShape { concept });
}
}
}
fn extract_group_attributes(filler: &ClassExpression) -> Option<Vec<Attribute>> {
match filler {
ClassExpression::ObjectSomeValuesFrom { attribute, filler } => match filler.as_ref() {
ClassExpression::Concept(value) => Some(vec![(*attribute, *value)]),
_ => None,
},
ClassExpression::ObjectIntersectionOf(ops) => {
let mut attrs = Vec::with_capacity(ops.len());
for op in ops {
match op {
ClassExpression::ObjectSomeValuesFrom { attribute, filler } => {
match filler.as_ref() {
ClassExpression::Concept(value) => attrs.push((*attribute, *value)),
_ => return None,
}
}
_ => return None,
}
}
Some(attrs)
}
_ => None,
}
}