use crate::{
BusinessProcessModelAndNotation,
element::{BPMNElement, BPMNElementTrait},
parser::parser_state::GlobalIndex,
semantics::{BPMNRootMarking, BPMNSubMarking, TransitionIndex},
sequence_flow::BPMNSequenceFlow,
structure_checker::verify_structural_correctness_initiation_mode,
traits::{
objectable::{BPMNObject, EMPTY_FLOWS},
processable::Processable,
searchable::Searchable,
startable::{InitiationMode, Startable},
transitionable::{
Transitionable, enabledness_xor_join_only, execute_transition_parallel_split, execute_transition_xor_join_consume, number_of_transitions_xor_join_only
},
},
};
use anyhow::{Context, Result, anyhow};
use bitvec::{bitvec, vec::BitVec};
use ebi_activity_key::Activity;
use ebi_arithmetic::{Fraction, One};
#[derive(Debug, Clone)]
pub struct BPMNExpandedSubProcess {
pub(crate) global_index: GlobalIndex,
pub(crate) id: String,
pub(crate) local_index: usize,
pub(crate) name: Option<String>,
pub(crate) elements: Vec<BPMNElement>,
pub(crate) sequence_flows: Vec<BPMNSequenceFlow>,
pub(crate) incoming_sequence_flows: Vec<usize>,
pub(crate) outgoing_sequence_flows: Vec<usize>,
}
impl BPMNExpandedSubProcess {
pub(crate) fn start_process_instance(
&self,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<BPMNSubMarking> {
let initiation_mode = self.initiation_mode(bpmn)?;
self.to_sub_marking(&initiation_mode)
}
}
impl BPMNElementTrait for BPMNExpandedSubProcess {
fn add_incoming_sequence_flow(&mut self, flow_index: usize) -> Result<()> {
self.incoming_sequence_flows.push(flow_index);
Ok(())
}
fn add_outgoing_sequence_flow(&mut self, flow_index: usize) -> anyhow::Result<()> {
self.outgoing_sequence_flows.push(flow_index);
Ok(())
}
fn add_incoming_message_flow(&mut self, _flow_index: usize) -> Result<()> {
Err(anyhow!(
"expanded sub-processes cannot have incoming message flows"
))
}
fn add_outgoing_message_flow(&mut self, _flow_index: usize) -> Result<()> {
Err(anyhow!(
"expanded sub-processes cannot have outgoing message flows"
))
}
fn verify_structural_correctness(
&self,
_parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<()> {
for element in &self.elements {
element.verify_structural_correctness(self, bpmn)?
}
verify_structural_correctness_initiation_mode!(self, bpmn);
Ok(())
}
}
impl BPMNObject for BPMNExpandedSubProcess {
fn global_index(&self) -> GlobalIndex {
self.global_index
}
fn id(&self) -> &str {
&self.id
}
fn local_index(&self) -> usize {
self.local_index
}
fn is_unconstrained_start_event(
&self,
_bpmn: &BusinessProcessModelAndNotation,
) -> Result<bool> {
Ok(false)
}
fn is_end_event(&self) -> bool {
false
}
fn incoming_sequence_flows(&self) -> &[usize] {
&self.incoming_sequence_flows
}
fn outgoing_sequence_flows(&self) -> &[usize] {
&self.outgoing_sequence_flows
}
fn incoming_message_flows(&self) -> &[usize] {
&EMPTY_FLOWS
}
fn outgoing_message_flows(&self) -> &[usize] {
&EMPTY_FLOWS
}
fn can_start_process_instance(&self, _bpmn: &BusinessProcessModelAndNotation) -> Result<bool> {
Ok(self.incoming_sequence_flows().len() == 0)
}
fn outgoing_message_flows_always_have_tokens(&self) -> bool {
false
}
fn outgoing_messages_cannot_be_removed(&self) -> bool {
false
}
fn incoming_messages_are_ignored(&self) -> bool {
false
}
fn can_have_incoming_sequence_flows(&self) -> bool {
true
}
fn can_have_outgoing_sequence_flows(&self) -> bool {
true
}
}
impl Transitionable for BPMNExpandedSubProcess {
fn number_of_transitions(&self, marking: &BPMNSubMarking) -> usize {
let mut result = number_of_transitions_xor_join_only!(self);
for sub_marking in &marking.element_index_2_sub_markings[self.local_index] {
result += 1;
result += self.elements.number_of_transitions(sub_marking);
}
result
}
fn enabled_transitions(
&self,
root_marking: &BPMNRootMarking,
sub_marking: &BPMNSubMarking,
_parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<BitVec> {
let mut result = enabledness_xor_join_only!(self, sub_marking);
for sub_marking in &sub_marking.element_index_2_sub_markings[self.local_index] {
let sub_marking_enabled_transitions =
self.elements
.enabled_transitions(root_marking, sub_marking, self, bpmn)?;
if sub_marking_enabled_transitions.not_any() {
result.push(true);
} else {
result.push(false);
}
result.extend(sub_marking_enabled_transitions);
}
Ok(result)
}
fn execute_transition(
&self,
mut transition_index: TransitionIndex,
root_marking: &mut BPMNRootMarking,
sub_marking: &mut BPMNSubMarking,
_parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<()> {
if transition_index < number_of_transitions_xor_join_only!(self) {
execute_transition_xor_join_consume!(self, sub_marking, transition_index);
sub_marking.element_index_2_sub_markings[self.local_index]
.push(self.start_process_instance(bpmn)?);
return Ok(());
}
transition_index -= number_of_transitions_xor_join_only!(self);
let mut remove_instantiation = None;
for (instantiation_index, sub_sub_marking) in sub_marking.element_index_2_sub_markings
[self.local_index]
.iter_mut()
.enumerate()
{
if transition_index == 0 {
remove_instantiation = Some(instantiation_index);
execute_transition_parallel_split!(self, sub_marking);
break;
}
transition_index -= 1;
let number_of_sub_transitions = self.elements.number_of_transitions(sub_sub_marking);
if transition_index < number_of_sub_transitions {
self.elements
.execute_transition(transition_index, root_marking, sub_sub_marking, self, bpmn)
.with_context(|| format!("Execute transition in sub-process `{}`.", self.id))?;
return Ok(());
}
transition_index -= number_of_sub_transitions;
}
if let Some(remove_instantiation_index) = remove_instantiation {
sub_marking.element_index_2_sub_markings[self.local_index]
.remove(remove_instantiation_index);
}
Ok(())
}
fn transition_activity(
&self,
mut transition_index: TransitionIndex,
marking: &BPMNSubMarking,
) -> Option<Activity> {
if transition_index < number_of_transitions_xor_join_only!(self) {
return None;
}
transition_index -= number_of_transitions_xor_join_only!(self);
for sub_marking in &marking.element_index_2_sub_markings[self.local_index] {
if transition_index == 0 {
return None;
}
transition_index -= 1;
let sub_number_of_transitions = self.elements.number_of_transitions(&sub_marking);
if transition_index < sub_number_of_transitions {
return self
.elements
.transition_activity(transition_index, &sub_marking);
}
transition_index -= sub_number_of_transitions;
}
None
}
fn transition_debug(
&self,
mut transition_index: TransitionIndex,
marking: &BPMNSubMarking,
bpmn: &BusinessProcessModelAndNotation,
) -> Option<String> {
if transition_index < self.incoming_sequence_flows.len().max(1) {
return Some(format!(
"expanded sub-process `{}`; start internal transition {}",
self.id, transition_index
));
}
transition_index -= self.incoming_sequence_flows.len().max(1);
for (i, sub_marking) in marking.element_index_2_sub_markings[self.local_index]
.iter()
.enumerate()
{
if transition_index == 0 {
return Some(format!(
"expanded sub-process `{}`; instantiation {}, end transition",
self.id, i
));
}
transition_index -= 1;
let sub_number_of_transitions = self.elements.number_of_transitions(&sub_marking);
if transition_index < sub_number_of_transitions {
return self
.elements
.transition_debug(transition_index, &sub_marking, bpmn);
}
transition_index -= sub_number_of_transitions;
}
None
}
fn transition_weight(
&self,
mut transition_index: TransitionIndex,
marking: &BPMNSubMarking,
_parent: &dyn Processable,
) -> Option<ebi_arithmetic::Fraction> {
if transition_index < self.incoming_sequence_flows.len().max(1) {
return Some(Fraction::one());
}
transition_index -= self.incoming_sequence_flows.len().max(1);
for sub_marking in marking.element_index_2_sub_markings[self.local_index].iter() {
if transition_index == 0 {
return Some(Fraction::one());
}
transition_index -= 1;
let sub_number_of_transitions = self.elements.number_of_transitions(&sub_marking);
if transition_index < sub_number_of_transitions {
return self
.elements
.transition_weight(transition_index, &sub_marking, self);
}
transition_index -= sub_number_of_transitions;
}
None
}
fn transition_2_marked_sequence_flows<'a>(
&'a self,
mut transition_index: TransitionIndex,
marking: &BPMNSubMarking,
_parent: &'a dyn Processable,
) -> Option<Vec<GlobalIndex>> {
if transition_index < number_of_transitions_xor_join_only!(self) {
return Some(vec![]);
}
transition_index -= number_of_transitions_xor_join_only!(self);
for sub_marking in &marking.element_index_2_sub_markings[self.local_index] {
if transition_index == 0 {
return Some(vec![]);
}
transition_index -= 1;
let sub_number_of_transitions = self.elements.number_of_transitions(&sub_marking);
if transition_index < sub_number_of_transitions {
return self.elements.transition_2_marked_sequence_flows(
transition_index,
&sub_marking,
self,
);
}
transition_index -= sub_number_of_transitions;
}
None
}
}
impl Startable for BPMNExpandedSubProcess {
fn unconstrained_start_events_without_recursing(
&self,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<Vec<&BPMNElement>> {
self.elements
.unconstrained_start_events_without_recursing(bpmn)
}
fn end_events_without_recursing(&self) -> Vec<&BPMNElement> {
self.elements.end_events_without_recursing()
}
fn start_elements_without_recursing(
&self,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<Vec<&BPMNElement>> {
self.elements.start_elements_without_recursing(bpmn)
}
}
impl Searchable for BPMNExpandedSubProcess {
fn id_2_pool_and_global_index(&self, id: &str) -> Option<(Option<usize>, GlobalIndex)> {
if self.id == id {
Some((Some(self.local_index), self.global_index))
} else {
if let Some((_, index)) = self.elements.id_2_pool_and_global_index(id) {
Some((Some(self.local_index), index))
} else {
None
}
}
}
fn global_index_2_sequence_flow_and_parent(
&self,
sequence_flow_global_index: GlobalIndex,
) -> Option<(&BPMNSequenceFlow, &dyn Processable)> {
for sequence_flow in &self.sequence_flows {
if sequence_flow.global_index == sequence_flow_global_index {
return Some((sequence_flow, self));
}
}
None
}
fn id_2_local_index(&self, id: &str) -> Option<usize> {
self.elements.id_2_local_index(id)
}
fn all_elements_ref(&self) -> Vec<&BPMNElement> {
self.elements.all_elements_ref()
}
fn parent_of(&self, global_index: GlobalIndex) -> (Option<&dyn Processable>, bool) {
if self.global_index == global_index {
(None, true)
} else {
let x = self.elements.parent_of(global_index);
if x.1 && x.0.is_none() {
(Some(self), true)
} else if x.1 {
x
} else {
(None, false)
}
}
}
fn all_sequence_flows_ref(&self) -> Vec<&BPMNSequenceFlow> {
let mut result: Vec<&BPMNSequenceFlow> = self.sequence_flows.iter().collect();
result.extend(self.elements.all_sequence_flows_ref());
result
}
fn global_index_2_sequence_flow_mut(
&mut self,
sequence_flow_global_index: GlobalIndex,
) -> Option<&mut BPMNSequenceFlow> {
let x = self
.sequence_flows
.iter_mut()
.filter_map(|sequence_flow| {
if sequence_flow.global_index == sequence_flow_global_index {
Some(sequence_flow)
} else {
None
}
})
.next();
if x.is_some() {
return x;
}
self.elements
.global_index_2_sequence_flow_mut(sequence_flow_global_index)
}
fn global_index_2_element(&self, index: GlobalIndex) -> Option<&BPMNElement> {
self.elements.global_index_2_element(index)
}
fn global_index_2_element_mut(&mut self, index: GlobalIndex) -> Option<&mut BPMNElement> {
self.elements.global_index_2_element_mut(index)
}
fn local_index_2_element_mut(&mut self, index: usize) -> Option<&mut BPMNElement> {
self.elements.local_index_2_element_mut(index)
}
}
macro_rules! to_sub_marking {
($self:ident, $initiation_mode:ident) => {
match $initiation_mode {
InitiationMode::ChoiceBetweenStartEvents() => {
Ok(BPMNSubMarking {
sequence_flow_2_tokens: vec![0; $self.sequence_flows_non_recursive().len()],
initial_choice_token: true,
element_index_2_tokens: vec![0; $self.elements_non_recursive().len()],
element_index_2_sub_markings: vec![
vec![];
$self.elements_non_recursive().len()
],
})
}
InitiationMode::ParallelElements(elements) => {
let mut element_index_2_tokens = vec![0; $self.elements_non_recursive().len()];
for element in elements {
element_index_2_tokens[element.local_index()] = 1;
}
Ok(BPMNSubMarking {
sequence_flow_2_tokens: vec![0; $self.sequence_flows_non_recursive().len()],
initial_choice_token: false,
element_index_2_tokens,
element_index_2_sub_markings: vec![
vec![];
$self.elements_non_recursive().len()
],
})
}
}
};
}
pub(crate) use to_sub_marking;
impl Processable for BPMNExpandedSubProcess {
fn elements_non_recursive(&self) -> &Vec<BPMNElement> {
&self.elements
}
fn sequence_flows_non_recursive(&self) -> &Vec<BPMNSequenceFlow> {
&self.sequence_flows
}
fn to_sub_marking(&self, initiation_mode: &InitiationMode) -> Result<BPMNSubMarking> {
to_sub_marking!(self, initiation_mode)
}
fn is_sub_process(&self) -> bool {
true
}
}