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