File Coverage

lib/BPM/XPDL.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             # Copyrights 2009-2015 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5 7     7   131110 use warnings;
  7         14  
  7         212  
6 7     7   33 use strict;
  7         13  
  7         215  
7              
8             package BPM::XPDL;
9 7     7   35 use vars '$VERSION';
  7         8  
  7         443  
10             $VERSION = '0.93';
11              
12 7     7   34 use base 'XML::Compile::Cache';
  7         14  
  7         5091  
13              
14             use XML::Compile::Util qw/type_of_node unpack_type pack_type/;
15             use Log::Report 'business-xpdl', syntax => 'SHORT';
16             use BPM::XPDL::Util;
17              
18              
19             # map namespace always to the newest implementation of the protocol
20             my %ns2version =
21             ( &NS_XPDL_009 => '0.09'
22             , &NS_XPDL_10 => '1.0'
23             , &NS_XPDL_20 => '2.0'
24             , &NS_XPDL_21 => '2.1'
25             , &NS_XPDL_22 => '2.2'
26             );
27              
28             my %info =
29             ( '0.01' => { } # not usable
30             , '0.09' =>
31             { prefixes => { '' => NS_XPDL_009 }
32             }
33             , '1.0' =>
34             { prefixes => { '' => NS_XPDL_10 }
35             }
36             , '2.0alpha-21' =>
37             { prefixes => { '' => NS_XPDL_20 }
38             }
39             , '2.0alpha-24' =>
40             { prefixes => { '' => NS_XPDL_20 }
41             }
42             , '2.0' => # alpha namespace used for final product
43             { prefixes => { '' => NS_XPDL_20 }
44             }
45             , '2.1' =>
46             { prefixes => { '' => NS_XPDL_21 }
47             }
48             , '2.2' =>
49             { prefixes => { '' => NS_XPDL_22 }
50             }
51             );
52              
53             #--------
54              
55              
56             sub new($)
57             { my $class = shift;
58             $class->SUPER::new(direction => 'RW', @_);
59             }
60              
61             sub init($)
62             { my ($self, $args) = @_;
63             $args->{allow_undeclared} = 1
64             unless exists $args->{allow_undeclared};
65              
66             $self->SUPER::init($args);
67              
68             $self->anyElement('ATTEMPT');
69             $self->addCompileOptions(RW => sloppy_floats => 1, sloppy_integers => 1);
70             $self->addCompileOptions(READERS => mixed_elements => 'XML_NODE');
71              
72             my $version = $args->{version}
73             or error __x"XPDL object requires an explicit version";
74              
75             unless(exists $info{$version})
76             { exists $ns2version{$version}
77             or error __x"XPDL version {v} not recognized", v => $version;
78             $version = $ns2version{$version};
79             }
80             $self->{version} = $version;
81             my $info = $info{$version};
82             $self->{namespace} = $info->{prefixes}{''};
83              
84             my $prefix_keys = $self->{prefixed} = delete $args->{prefix_keys};
85              
86             $self->addPrefixes($info->{prefixes});
87             $self->addKeyRewrite('PREFIXES(xpdl)') if $prefix_keys;
88              
89             (my $xsd = __FILE__) =~ s!\.pm!/xsd!;
90             my @xsds = glob "$xsd/xpdl-$version/*";
91              
92             # support deprecated versions
93             if($version gt '1.0') # $version is a version label, not number
94             { trace "loading deprecated xpdl 1.0";
95             $self->addPrefixes(xpdl10 => NS_XPDL_10);
96             push @xsds, glob "$xsd/xpdl-1.0/*";
97             $self->addKeyRewrite('PREFIXES(xpdl10)') if $prefix_keys;
98              
99             # this trick is needed because the StartMode element became an
100             # attribute in the same structure
101             $self->addKeyRewrite(
102             { pack_type(NS_XPDL_10, 'StartMode' ) => 'dep_StartMode'
103             , pack_type(NS_XPDL_10, 'FinishMode') => 'dep_FinishMode'} );
104             }
105              
106             if($version ge '2.1')
107             { trace "loading deprecated xpdl 2.0";
108             $self->addPrefixes(xpdl20 => NS_XPDL_20);
109             push @xsds, glob "$xsd/xpdl-2.0/*";
110             $self->addKeyRewrite('PREFIXES(xpdl20)') if $prefix_keys;
111             }
112              
113             $self->importDefinitions(\@xsds);
114             $self;
115             }
116              
117              
118             sub from($@)
119             { my ($thing, $source, %args) = @_;
120              
121             my $xml = XML::Compile->dataToXML($source);
122             my $top = type_of_node $xml;
123             my ($ns, $topname) = unpack_type $top;
124             my $version = $ns2version{$ns}
125             or error __x"unknown XPDL version with namespace {ns}", ns => $ns;
126              
127             $topname eq 'Package'
128             or error __x"file does not contain a Package but {local}"
129             , local => $topname;
130              
131             my ($self, $convert);
132             if(ref $thing)
133             { # instance method
134             $self = $thing;
135              
136             ! $self->{prefixed}
137             or error __x"cannot use prefixed_keys with version conversion";
138              
139             $convert = 1;
140             }
141             else
142             { # class method: can determine version myself
143             $self = $thing->new(version => $version, %args);
144             $convert = 0;
145             }
146              
147             my $r = $self->reader($top, %args)
148             or error __x"root node `{top}' not recognized", top => $top;
149              
150             my $data = $r->($xml);
151              
152             if($convert)
153             { # upgrade structures. Even when the versions match, they may
154             # contain deprecated structures which can be removed.
155             $self->convert10to20($data)
156             if $self->version gt '1.0';
157              
158             $self->convert20to21($data)
159             if $self->version gt '2.0';
160             }
161              
162             (pack_type($self->namespace, 'Package'), , $data);
163             }
164              
165             sub convert10to20($)
166             { my ($self, $data) = @_;
167              
168             trace "Convert xpdl version from 1.0 to 2.0";
169              
170             # The conversions to be made are described in the XPDL specification
171             # documents. However, be aware that there are considerable additions.
172              
173             my $ns = $self->namespace;
174             my $prefix
175             = $ns eq NS_XPDL_20 ? 'xpdl20'
176             : $ns eq NS_XPDL_21 ? 'xpdl21'
177             : panic;
178              
179             # do not walk more than one HASH level at a time, to avoid creation
180             # of unused HASHes.
181             my $wfps = $data->{WorkflowProcesses} || {};
182             foreach my $wfp (@{$wfps->{WorkflowProcess} || []})
183             {
184             my $acts = $wfp->{Activities} || {};
185             foreach my $act (@{$acts->{Activity} || []})
186             { # Start/Finish mode from element -> attribute
187             if(my $sm = delete $act->{dep_StartMode})
188             { (my $mode, undef) = %$sm; # only 1 key-value pair!
189             $act->{StartMode} = $mode;
190             }
191             if(my $fm = delete $act->{dep_FinishMode})
192             { (my $mode, undef) = %$fm;
193             $act->{dep_FinishMode} = $mode;
194             }
195              
196             # BlockId -> ActivitySetId
197             if(my $ba = $act->{BlockActivity})
198             { # rename option BlockId into ActivitySetId
199             $ba->{ActivitySetId} = delete $ba->{BlockId}
200             if $ba->{BlockId};
201             }
202              
203             # DeadlineCondition -> DeadlineDuration
204             foreach my $dead (@{$act->{Deadline} || []})
205             { $dead->{DeadlineDuration} = delete $dead->{DeadlineCondition}
206             if $dead->{DeadlineCondition};
207             }
208              
209             # Remove Tool attribute "Type"
210             if(my $impl = $act->{Implementation})
211             { if(my $tools = $impl->{Tool})
212             { delete $_->{Type} for @$tools;
213             }
214             }
215             }
216              
217             # remove Index attribute to FormalParameter
218             my $fps = $wfp->{FormalParameters} || {};
219             foreach my $param (@{$fps->{FormalParameter} || []})
220             { delete $param->{Index};
221             }
222              
223             my $appls = $wfp->{Applications} || {};
224             foreach my $appl (@{$appls->{Application} || []})
225             { my $afps = $appl->{FormalParameters} || {};
226             for my $param (@{$afps->{FormalParameter}||[]})
227             { delete $param->{Index};
228             }
229             }
230            
231             # Condition/Xpression to Condition/Expression
232             my $trs = $wfp->{Transitions} || {};
233             for my $trans (@{$trs->{Transition} || []})
234             { my $cond = $trans->{Condition} or next;
235             foreach ($cond->getChildrenByLocalName('Xpression'))
236             { $_->setNodeName('Expression');
237             $_->setNamespace($ns, $prefix, 1);
238             }
239             }
240              
241             my $sets = $wfp->{ActivitySets} || {};
242             foreach my $set (@{$sets->{ActivitySet} || []})
243             { my $strans = $set->{Transitions} || {};
244             foreach my $trans (@{$strans->{Transition} || []})
245             { my $cond = $trans->{Condition} or next;
246             foreach ($cond->getChildrenByLocalName('Xpression'))
247             { $_->setNodeName('Expression');
248             $_->setNamespace($ns, $prefix, 1);
249             }
250             }
251             }
252              
253             # Order in WorkflowProcess changed. This is a no-op for X::C
254             }
255              
256             $data->{PackageHeader}{XPDLVersion} = '2.0';
257             $data;
258             }
259              
260             sub convert20to21($)
261             { my ($self, $data) = @_;
262              
263             trace "Convert xpdl version from 2.0 to 2.1";
264              
265             # Tool has been removed from the spec. However, it can still be
266             # used in the old namespace, and I do not know how to convert it
267             # to 2.1 structures (yet)
268              
269             my $ns = $self->namespace;
270             my $prefix
271             = $ns eq NS_XPDL_21 ? 'xpdl21'
272             : panic;
273              
274              
275             # do not walk more than one HASH level at a time, to avoid creation
276             # of unused HASHes.
277             my $wfps = $data->{WorkflowProcesses} || {};
278             foreach my $wfp (@{$wfps->{WorkflowProcess} || []})
279             {
280             my $acts = $wfp->{Activities} || {};
281             foreach my $act (@{$acts->{Activity} || []})
282             { # Rewrite Tool to Task/TaskApplication
283             if(my $impl = $act->{Implementation})
284             { foreach my $tool (@{delete $impl->{Tool} || []})
285             { my %task = %$tool;
286             delete $task{PackageRef}; # ?relocate info?
287             delete $task{ExtendedAttributes}; # ?into DataMapping?
288             delete $task{Type}; # shouldn't be there, rem in 2.0
289             $impl->{Task}{TaskApplication} = \%task;
290             }
291             }
292             }
293              
294             # Condition/Xpression to Condition/Expression
295             my $trs = $wfp->{Transitions} || {};
296             for my $trans (@{$trs->{Transition} || []})
297             { my $cond = $trans->{Condition} or next;
298             foreach ($cond->getChildrenByLocalName('Expression'))
299             { $_->setNamespace($ns, $prefix, 1);
300             }
301             }
302              
303             my $sets = $wfp->{ActivitySets} || {};
304             foreach my $set (@{$sets->{ActivitySet} || []})
305             { my $strans = $set->{Transitions} || {};
306             foreach my $trans (@{$strans->{Transition} || []})
307             { my $cond = $trans->{Condition} or next;
308             foreach ($cond->getChildrenByLocalName('Expression'))
309             { $_->setNamespace($ns, $prefix, 1);
310             }
311             }
312             }
313             }
314              
315             $data->{PackageHeader}{XPDLVersion} = '2.1';
316             $data;
317             }
318              
319             #----------
320              
321              
322             sub version() {shift->{version}}
323             sub namespace() {shift->{namespace}}
324              
325             #--------
326              
327             sub create($)
328             { my ($self, $data) = @_;
329             my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
330             my $wr = $self->writer('Package')
331             or panic "cannot find Package type";
332              
333             my $root = $wr->($doc, $data);
334             $doc->setDocumentElement($root);
335             $doc;
336             }
337              
338             1;