1818# 02110-1301 USA
1919
2020from .ValidationException import ValidationException
21- from ..specs .bpmn_process_spec import BpmnProcessSpec
21+ from ..specs .bpmn_process_spec import BpmnProcessSpec , AdHocSubprocessSpec
2222from ..specs .data_spec import DataObject
2323from ..specs .control import StartEventJoin , StartEventSplit
2424from .node_parser import NodeParser
25- from .util import first
25+ from .util import first , full_tag
2626
2727
2828class ProcessParser (NodeParser ):
@@ -156,14 +156,8 @@ def parse_node(self, node):
156156 return task_spec
157157
158158 def _parse (self ):
159- # here we only look in the top level, We will have another
160- # bpmn:startEvent if we have a subworkflow task
161- start_node_list = self .xpath ('./bpmn:startEvent' )
162- if not start_node_list and self .process_executable :
163- raise ValidationException ("No start event found" , node = self .node , file_name = self .filename )
164- if not self .process_executable :
165- raise ValidationException (f"Process { self .bpmn_id } is not executable." , node = self .node , file_name = self .filename )
166- self .spec = BpmnProcessSpec (name = self .bpmn_id , description = self .get_name (), filename = self .filename )
159+
160+ self .spec = self .create_spec ()
167161
168162 # Get the data objects
169163 for obj in self .xpath ('./bpmn:dataObject' ):
@@ -175,9 +169,20 @@ def _parse(self):
175169 if io_spec is not None :
176170 self .spec .io_specification = self .parse_io_spec ()
177171
178- # set the data stores on the process spec so they can survive
179- # serialization
180172 self .spec .data_stores = self .data_stores
173+ self .add_tasks ()
174+
175+ def create_spec (self ):
176+ if not self .process_executable :
177+ raise ValidationException (f"Process { self .bpmn_id } is not executable." , node = self .node , file_name = self .filename )
178+ return BpmnProcessSpec (name = self .bpmn_id , description = self .get_name (), filename = self .filename )
179+
180+ def add_tasks (self ):
181+
182+ start_node_list = self .xpath ('./bpmn:startEvent' )
183+ if not start_node_list and self .process_executable :
184+ raise ValidationException ("No start event found" , node = self .node , file_name = self .filename )
185+
181186 for node in start_node_list :
182187 self .parse_node (node )
183188
@@ -207,3 +212,44 @@ def get_spec(self):
207212 if self .spec is None :
208213 self ._parse ()
209214 return self .spec
215+
216+ class AdHocParser (ProcessParser ):
217+
218+ def _parse (self ):
219+ super ()._parse ()
220+ self .spec .create_paths ()
221+
222+ def create_spec (self ):
223+ if self .attribute ('ordering' ) == 'sequential' :
224+ raise ValidationException (
225+ 'Sequential ordering for ad hoc subprocesses not supported' ,
226+ node = self .node ,
227+ file_name = self .filename
228+ )
229+ cancel_remaining = self .attribute ('cancelRemainingInstances' ) in [None , 'true' ]
230+ condition = self .xpath ('./bpmn:completionCondition' )
231+ condition = condition [0 ].text if len (condition ) > 0 else None
232+ return AdHocSubprocessSpec (
233+ completion_condition = condition ,
234+ cancel_remaining = cancel_remaining ,
235+ name = self .bpmn_id ,
236+ description = self .get_name (),
237+ filename = self .filename ,
238+ )
239+
240+ def add_tasks (self ):
241+ start_node_list = []
242+ for node in self .node .getchildren ():
243+ if node .tag not in self .parser .PARSER_CLASSES :
244+ continue
245+ elif node .tag in [full_tag ('startEvent' ), full_tag ('endEvent' )]:
246+ raise ValidationException (
247+ 'Ad hoc subprocesses may not contain start or end events' ,
248+ node = self .node ,
249+ file_name = self .filename ,
250+ )
251+ elif len (node .xpath ('./bpmn:incoming' , namespaces = self .nsmap )) == 0 :
252+ start_node_list .append (node )
253+
254+ for node in start_node_list :
255+ self .parse_node (node )
0 commit comments