use crate::core::event_data::object_centric::linked_ocel::{
slim_linked_ocel::{EventOrObjectIndex, SlimLinkedOCEL},
LinkedOCELAccess,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
pub type Qualifier = String;
pub type TypeName = String;
pub type EntityRef = EventOrObjectIndex;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub enum EntityKind {
Event,
Object,
}
#[derive(
Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
pub enum TypeRef {
Event(TypeName),
Object(TypeName),
}
impl TypeRef {
pub fn name(&self) -> &str {
match self {
TypeRef::Event(name) | TypeRef::Object(name) => name,
}
}
pub fn kind(&self) -> EntityKind {
match self {
TypeRef::Event(_) => EntityKind::Event,
TypeRef::Object(_) => EntityKind::Object,
}
}
pub fn is_event(&self) -> bool {
matches!(self, TypeRef::Event(_))
}
}
impl std::fmt::Display for TypeRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct TypeEdge {
pub source: TypeRef,
pub target: TypeRef,
pub qualifier: Qualifier,
}
impl std::fmt::Display for TypeEdge {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let prefix = |kind| match kind {
EntityKind::Event => "E",
EntityKind::Object => "O",
};
write!(
f,
"{}:{} --[{}]--> {}:{}",
prefix(self.source.kind()),
self.source.name(),
self.qualifier,
prefix(self.target.kind()),
self.target.name()
)
}
}
#[derive(Debug, Clone)]
pub struct TypeGraph {
pub event_types: Vec<TypeName>,
pub object_types: Vec<TypeName>,
pub edges: Vec<TypeEdge>,
pub adj: HashMap<TypeRef, Vec<usize>>,
pub rev_adj: HashMap<TypeRef, Vec<usize>>,
}
impl TypeGraph {
pub fn from_linked_ocel(ocel: &SlimLinkedOCEL) -> Self {
let event_types: Vec<TypeName> = ocel.get_ev_types().map(|s| s.to_string()).collect();
let object_types: Vec<TypeName> = ocel.get_ob_types().map(|s| s.to_string()).collect();
let mut edge_set: HashSet<(EntityKind, &str, EntityKind, &str, &str)> = HashSet::new();
for ev in ocel.get_all_evs() {
let ev_type = ocel.get_ev_type_of(&ev);
for (qualifier, ob) in ocel.get_e2o(&ev) {
let ob_type = ocel.get_ob_type_of(ob);
edge_set.insert((
EntityKind::Event,
ev_type,
EntityKind::Object,
ob_type,
qualifier,
));
}
}
for ob in ocel.get_all_obs() {
let ob_type = ocel.get_ob_type_of(&ob);
for (qualifier, target_ob) in ocel.get_o2o(&ob) {
let target_type = ocel.get_ob_type_of(target_ob);
edge_set.insert((
EntityKind::Object,
ob_type,
EntityKind::Object,
target_type,
qualifier,
));
}
}
let make_ref = |kind, name: &str| match kind {
EntityKind::Event => TypeRef::Event(name.to_string()),
EntityKind::Object => TypeRef::Object(name.to_string()),
};
let mut edges: Vec<TypeEdge> = edge_set
.into_iter()
.map(|(src_kind, source, tgt_kind, target, qualifier)| TypeEdge {
source: make_ref(src_kind, source),
target: make_ref(tgt_kind, target),
qualifier: qualifier.to_string(),
})
.collect();
edges.sort_by(|a, b| {
(&a.source, &a.target, &a.qualifier).cmp(&(&b.source, &b.target, &b.qualifier))
});
let mut adj: HashMap<TypeRef, Vec<usize>> = HashMap::new();
let mut rev_adj: HashMap<TypeRef, Vec<usize>> = HashMap::new();
for (i, edge) in edges.iter().enumerate() {
adj.entry(edge.source.clone()).or_default().push(i);
rev_adj.entry(edge.target.clone()).or_default().push(i);
}
TypeGraph {
event_types,
object_types,
edges,
adj,
rev_adj,
}
}
pub fn neighbors_undirected(&self, type_ref: &TypeRef) -> Vec<(usize, &TypeRef, bool)> {
let forward = self
.adj
.get(type_ref)
.into_iter()
.flatten()
.map(|&idx| (idx, &self.edges[idx].target, false));
let reverse = self
.rev_adj
.get(type_ref)
.into_iter()
.flatten()
.map(|&idx| (idx, &self.edges[idx].source, true));
forward.chain(reverse).collect()
}
}
pub fn entity_type<'a>(ocel: &'a SlimLinkedOCEL, entity: &EntityRef) -> &'a str {
match entity {
EntityRef::Event(e) => ocel.get_ev_type_of(e),
EntityRef::Object(o) => ocel.get_ob_type_of(o),
}
}
pub fn entity_id<'a>(ocel: &'a SlimLinkedOCEL, entity: &EntityRef) -> &'a str {
match entity {
EntityRef::Event(e) => ocel.get_ev_id(e),
EntityRef::Object(o) => ocel.get_ob_id(o),
}
}
pub fn get_neighbors_via_edge(
ocel: &SlimLinkedOCEL,
entity: &EntityRef,
type_edge: &TypeEdge,
reverse: bool,
) -> Vec<EntityRef> {
let mut result = Vec::new();
if !reverse {
match (entity, type_edge.source.kind(), type_edge.target.kind()) {
(EntityRef::Event(ev), EntityKind::Event, EntityKind::Object) => {
for (qual, ob) in ocel.get_e2o_of_type(ev, type_edge.target.name()) {
if qual == type_edge.qualifier {
result.push(EntityRef::Object(*ob));
}
}
}
(EntityRef::Object(ob), EntityKind::Object, EntityKind::Object) => {
for (qual, target_ob) in ocel.get_o2o_of_type(ob, type_edge.target.name()) {
if qual == type_edge.qualifier {
result.push(EntityRef::Object(*target_ob));
}
}
}
_ => {}
}
} else {
match (entity, type_edge.target.kind(), type_edge.source.kind()) {
(EntityRef::Object(ob), EntityKind::Object, EntityKind::Event) => {
for (qual, ev) in ocel.get_e2o_rev_of_type(ob, type_edge.source.name()) {
if qual == type_edge.qualifier {
result.push(EntityRef::Event(*ev));
}
}
}
(EntityRef::Object(ob), EntityKind::Object, EntityKind::Object) => {
for (qual, source_ob) in ocel.get_o2o_rev_of_type(ob, type_edge.source.name()) {
if qual == type_edge.qualifier {
result.push(EntityRef::Object(*source_ob));
}
}
}
_ => {}
}
}
result
}