File Coverage

lib/Petal/Canonicalizer/XML.pm
Criterion Covered Total %
statement 290 295 98.3
branch 70 80 87.5
condition 49 58 84.4
subroutine 26 26 100.0
pod 0 4 0.0
total 435 463 93.9


line stmt bran cond sub pod time code
1             # ------------------------------------------------------------------
2             # Petal::Canonicalizer::XML - Builds an XML canonical Petal file
3             # ------------------------------------------------------------------
4             # Author: Jean-Michel Hiver
5             # Description: This modules mainly implements the XML::Parser
6             # 'Stream' interface. It receives XML events and builds Petal
7             # canonical data, i.e.
8             #
9             # Hello
10             #
11             # Might be canonicalized to something like
12             #
13             #
14             # Hello
15             #
16             # ------------------------------------------------------------------
17             package Petal::Canonicalizer::XML;
18 77     77   257 use Petal::Hash::String;
  77         75  
  77         1703  
19 77     77   27372 use MKDoc::XML::Encode;
  77         12658  
  77         1557  
20 77     77   305 use strict;
  77         78  
  77         1089  
21 77     77   220 use warnings;
  77         76  
  77         1525  
22              
23 77     77   216 use vars qw /@Result @NodeStack/;
  77         80  
  77         205338  
24              
25              
26             # $class->process ($parser, $data_ref);
27             # -------------------------------------
28             # returns undef if $parser object (i.e. a Petal::Parser::XML object)
29             # could not parse the data which $data_ref pointed to.
30             #
31             # returns a reference to the canonicalized string otherwise.
32             sub process
33             {
34 206     206 0 238 my $class = shift;
35 206         299 my $parser = shift;
36 206         251 my $data_ref = shift;
37 206 50       489 $data_ref = (ref $data_ref) ? $data_ref : \$data_ref;
38            
39             # grab anything that's before the first '<' tag
40 206         1243 my ($header) = $$data_ref =~ /(^.*?)<(?!\?|\!)/sm;
41 206         1727 $$data_ref =~ s/(^.*?)<(?!\?|\!)/\
42            
43             # grab the tags which the parser is going to strip
44             # in order to reinclude them afterwards
45             # my @decls = $$data_ref =~ /()/gsm;
46            
47             # take the existing processing instructions out and replace
48             # them with temporary xml-friendly handlers
49 206         544 my $pis = $class->_processing_instructions_out ($data_ref);
50            
51 206         398 local @Result = ();
52 206         258 local @NodeStack = ();
53            
54 206         541 $parser->process ($class, $data_ref);
55            
56 202   100     680 $header ||= '';
57 202         195 my $res = '';
58 202 100       524 $res .= $header unless ($Petal::CURRENT_INCLUDES > 1);
59 202         1245 $res .= (join '', @Result);
60              
61 202         623 $class->_processing_instructions_in (\$res, $pis);
62            
63 202         1336 return \$res;
64             }
65              
66              
67             # _processing_instructions_out ($data_ref);
68             # -----------------------------------------
69             # takes the existing processing instructions (i.e. )
70             # and replace them with temporary xml-friendly handlers (i.e.
71             # [-- NBXNBBJBNJNBJVNK --]
72             #
73             # returns the => [-- NBXNBBJBNJNBJVNK --] mapping
74             # as a hashref
75             #
76             # NOTE: This is because processing instructions are special to
77             # HTML::Parser, XML::Parser etc. and it's easier to just handle
78             # them separately
79             sub _processing_instructions_out
80             {
81 206     206   235 my $class = shift;
82 206         212 my $data_ref = shift;
83 206         829 my %pis = map { $_ => $class->_compute_unique_string ($data_ref) } $$data_ref =~ /(<\?.*?\?>)/gsm;
  60         113  
84            
85 206         774 while (my ($key, $value) = each %pis) {
86 49         1254 $$data_ref =~ s/\Q$key\E/$value/gsm;
87             }
88            
89 206         323 return \%pis;
90             }
91              
92              
93             # _processing_instructions_in ($data_ref, $pis);
94             # ----------------------------------------------
95             # takes the processing instructions mapping defined in the $pis
96             # hashref and restores the processing instructions in the data
97             # pointed by $data_ref
98             sub _processing_instructions_in
99             {
100 202     202   236 my $class = shift;
101 202         200 my $data_ref = shift;
102 202         196 my $pis = shift;
103 202         227 while (my ($key, $value) = each %{$pis}) {
  251         787  
104 49         1128 $$data_ref =~ s/\Q$value\E/$key/gsm;
105             }
106             }
107              
108              
109             # _compute_unique_string ($data_ref)
110             # ----------------------------------
111             # computes a string which does not exist in $$data_ref
112             sub _compute_unique_string
113             {
114 60     60   54 my $class = shift;
115 60         49 my $data_ref = shift;
116 60         81 my $string = '[-' . (join '', map { chr (ord ('a') + int rand 26) } 1..20) . '-]';
  1200         1567  
117 60         327 while (index ($$data_ref, $string) >= 0)
118             {
119 0         0 $string = '[-' . (join '', map { chr (ord ('a') + int rand 26) } 1..20) . '-]';
  0         0  
120             }
121 60         168 return $string;
122             }
123              
124              
125             # $class->StartTag();
126             # -------------------
127             # Called for every start tag with a second parameter of the element type.
128             # It will check for special PETAL attributes like petal:if, petal:loop, etc...
129             # and rewrite the start tag into @Result accordingly.
130             #
131             # For example
132             #
133             #
134             #
135             # Is rewritten
136             #
137             # ...
138             sub StartTag
139             {
140 1306     1306 0 2248 Petal::load_code_generator(); # we will use it later
141            
142 1306         1154 my $class = shift;
143 1306         1531 push @NodeStack, {};
144 1306 100       2362 return if ($class->_is_inside_content_or_replace());
145            
146 1299         1133 my $tag = $_;
147 1299         4756 ($tag) = $tag =~ /^<\s*((?:\w|\:|\-)*)/;
148 1299         3212 my $att = { %_ };
149            
150 1299         2353 $class->_use_macro ($tag, $att);
151 1299         1910 $class->_on_error ($tag, $att);
152 1299         1880 $class->_define ($tag, $att);
153 1299         2027 $class->_define_slot ($tag, $att);
154 1299         1810 $class->_condition ($tag, $att);
155 1299         1969 $class->_repeat ($tag, $att);
156 1299 100 66     1948 $class->_is_xinclude ($tag) and $class->_xinclude ($tag, $att) and return;
157 1253         2023 $class->_replace ($tag, $att);
158            
159 1253         1109 my $petal = quotemeta ($Petal::NS);
160            
161             # if a petal:replace attribute was set, then at this point _is_inside_content_or_replace()
162             # should return TRUE and this code should not be executed
163 1253 100       1682 unless ($class->_is_inside_content_or_replace())
164             {
165             # for every attribute which is not a petal: attribute,
166             # we need to convert $variable into
167 1224         884 foreach my $key (keys %{$att})
  1224         3070  
168             {
169 2008 100       4565 next if ($key =~ /^$petal:/);
170 1922         2040 my $text = $att->{$key};
171 1922         1365 my $token_re = $Petal::Hash::String::TOKEN_RE;
172 1922         4232 my @vars = $text =~ /$token_re/gsm;
173 1922         1925 my %vars = map { $_ => 1 } @vars;
  3         12  
174 1922         1988 @vars = sort { length ($b) <=> length ($a) } keys %vars;
  0         0  
175 1922         2003 foreach my $var (@vars)
176             {
177 3         8 my $command = $var;
178 3         15 $command =~ s/^\$//;
179 3         8 $command =~ s/^\{//;
180 3         20 $command =~ s/\}$//;
181 3         9 $command = $class->_encode_backslash_semicolon ($command);
182 3         9 $command = "";
183 3         34 $text =~ s/\Q$var\E/$command/g;
184             }
185 1922         2924 $att->{$key} = $text;
186             }
187              
188             # processes the petal:attributes instruction
189 1224         2397 $class->_attributes ($tag, $att);
190            
191 1224         1278 my @att_str = ();
192 1224         926 foreach my $key (keys %{$att})
  1224         2154  
193             {
194 1987 100       4311 next if ($key =~ /^$petal:/);
195 1930         2084 my $value = $att->{$key};
196 1930 100       2289 if ($value =~ /^<\?attr/)
197             {
198 45         72 push @att_str, $value;
199             }
200             else
201             {
202 1885         3245 my $tokens = Petal::CodeGenerator->_tokenize (\$value);
203             my @res = map {
204             ($_ =~ /$Petal::CodeGenerator::PI_RE/s) ?
205             $_ :
206 1884 100       3596 do {
207 1881         1769 $_ =~ s/\&/&/g;
208 1881         1497 $_ =~ s/\
209 1881         1410 $_ =~ s/\>/>/g;
210 1881         1397 $_ =~ s/\"/"/g;
211 1881         2823 $_;
212             };
213 1885         1318 } @{$tokens};
  1885         1819  
214 1885         5470 push @att_str, $key . '="' . (join '', @res) . '"';
215             }
216             }
217            
218 1224         1841 my $att_str = join " ", @att_str;
219              
220 1224 100       2271 if (defined $att->{"$petal:omit-tag"})
221             {
222 5   100     26 my $expression = $att->{"$petal:omit-tag"} || 'string:1';
223 5         13 $NodeStack[$#NodeStack]->{'omit-tag'} = $expression;
224 5 100 66     40 push @Result, (defined $att_str and $att_str) ?
225             "<$tag $att_str>" :
226             "<$tag>";
227             }
228             else
229             {
230 1219 100 66     5160 push @Result, (defined $att_str and $att_str) ? "<$tag $att_str>" : "<$tag>";
231             }
232            
233 1224         2354 $class->_content ($tag, $att);
234             }
235             }
236              
237              
238             # $class->EndTag();
239             # -----------------
240             # Called for every end tag with a second parameter of the element type.
241             # It will check in the @NodeStack to see if this end-tag also needs to close
242             # some 'condition' or 'repeat' statements, i.e.
243             #
244             #
245             #
246             # Could be rewritten
247             #
248             #
249             #
250             # If the starting LI used a loop, i.e.
  • 251             sub EndTag
    252             {
    253 1306     1306 0 1112 my $class = shift;
    254 1306 100       1711 return if ($class->_is_inside_content_or_replace ( 'endtag' ));
    255            
    256 1294         4780 my ($tag) = $_ =~ /^<\/\s*((?:\w|\:|\-)*)/;
    257 1294         1457 my $node = pop (@NodeStack);
    258            
    259 1294 100       1978 return if ($class->_is_xinclude ($tag));
    260            
    261 1248 100 66     2620 unless (defined $node->{replace} and $node->{replace})
    262             {
    263 1224 100       1553 if (exists $node->{'omit-tag'})
    264             {
    265 5         6 my $expression = $node->{'omit-tag'};
    266 5         15 push @Result, "";
    267             }
    268             else
    269             {
    270 1219         2155 push @Result, "";
    271             }
    272             }
    273            
    274 1248   100     3362 my $repeat = $node->{repeat} || '0';
    275 1248   100     2656 my $condition = $node->{condition} || '0';
    276 1248   100     2680 my $define_slot = $node->{define_slot} || '0';
    277 1248         2472 push @Result, map { '' } 1 .. ($repeat+$condition+$define_slot);
      58         98  
    278              
    279 1248 100 66     2754 unless (defined $node->{replace} and $node->{replace})
    280             {
    281 1224 100       2911 if (exists $node->{'on-error'})
    282             {
    283 4         5 my $expression = $node->{'on-error'};
    284 4         15 push @Result, "";
    285             }
    286             }
    287             }
    288              
    289              
    290             # $class->Text();
    291             # ---------------
    292             # Called just before start or end tags.
    293             # Turns all variables such as $foo:bar into
    294             sub Text
    295             {
    296 2118     2118 0 1622 my $class = shift;
    297 2118 100       2554 return if ($class->_is_inside_content_or_replace());
    298 2017         1696 my $text = $_;
    299 2017         1560 my $token_re = $Petal::Hash::String::TOKEN_RE;
    300 2017         6748 my @vars = $text =~ /$token_re/gsm;
    301 2017         2385 my %vars = map { $_ => 1 } @vars;
      64         181  
    302 2017         2762 @vars = sort { length ($b) <=> length ($a) } keys %vars;
      28         72  
    303 2017         2322 foreach my $var (@vars)
    304             {
    305 64         77 my $command = $var;
    306 64         208 $command =~ s/^\$//;
    307 64         95 $command =~ s/^\{//;
    308 64         91 $command =~ s/\}$//;
    309 64         122 $command = $class->_encode_backslash_semicolon ($command);
    310 64         150 $command = "";
    311 64         797 $text =~ s/\Q$var\E/$command/g;
    312             }
    313 2017         5797 push @Result, $text;
    314             }
    315              
    316              
    317             # _is_inside_content_or_replace();
    318             # --------------------------------
    319             # Returns TRUE if @NodeStack contains a node which has a
    320             # 'content' or a 'replace' attribute set.
    321             sub _is_inside_content_or_replace
    322             {
    323 18855     18855   12360 my $class = shift;
    324 18855         11687 my $endtag = shift;
    325 18855         11170 my $tmp = undef;
    326 18855 100       21049 $tmp = pop (@NodeStack) if ($endtag);
    327            
    328             # WHY do I have to do this?
    329 18855 100 66     22856 return 1 if (defined $tmp and $tmp->{'use-macro'});
    330 18846         28609 for (my $i=@NodeStack - 1; $i >= 0; $i--)
    331             {
    332             return 1 if ( defined $NodeStack[$i]->{'replace'} or
    333             defined $NodeStack[$i]->{'content'} or
    334 63943 100 100     275162 defined $NodeStack[$i]->{'use-macro'} );
          100        
    335             }
    336 18631 100       20904 push @NodeStack, $tmp if (defined $tmp);
    337 18631         26710 return;
    338             }
    339              
    340              
    341             # _split_expression ($expr);
    342             # --------------------------
    343             # Splits multiple semicolon separated expressions, which
    344             # are mainly used for the petal:attributes attribute, i.e.
    345             # would turn "href document.uri; lang document.lang; xml:lang document.lang"
    346             # into ("href document.uri", "lang document.lang", "xml:lang document.lang")
    347             sub _split_expression
    348             {
    349 220     220   207 my $class = shift;
    350 220         186 my $expression = shift;
    351 220 100 66     855 my @tokens = map { (defined $_ and $_) ? $_ : () }
      296         1039  
    352             split /(\s|\r|\n)*(?
    353             $expression;
    354            
    355 220         264 return map { s/^(\s|\n|\r)+//sm;
      270         564  
    356 270         993 s/(\s|\n|\r)+$//sm;
    357 270 100       699 ($_ eq '') ? () : $_ } @tokens;
    358             }
    359              
    360              
    361             # _condition;
    362             # -----------
    363             # Rewrites statements into
    364             #
    365             sub _on_error
    366             {
    367 1411     1411   1073 my $class = shift;
    368 1411 100       1603 return if ($class->_is_inside_content_or_replace());
    369            
    370 1402         1459 my $petal = quotemeta ($Petal::NS);
    371 1402         1200 my $tag = shift;
    372 1402         921 my $att = shift;
    373 1402   100     2742 my $expr = delete $att->{"$petal:on-error"} || return;
    374            
    375 4         10 $expr = $class->_encode_backslash_semicolon ($expr);
    376 4         7 push @Result, "";
    377 4         9 $NodeStack[$#NodeStack]->{'on-error'} = $expr;
    378 4         5 return 1;
    379             }
    380              
    381              
    382             # _define;
    383             # --------
    384             # Rewrites statements into
    385             # canonical
    386             sub _define
    387             {
    388 1411     1411   1164 my $class = shift;
    389 1411 100       1636 return if ($class->_is_inside_content_or_replace());
    390            
    391 1402         1152 my $petal = $Petal::NS;
    392 1402         1101 my $tag = shift;
    393 1402         1029 my $att = shift;
    394             my $expr = delete $att->{"$petal:set"} ||
    395             delete $att->{"$petal:def"} ||
    396 1402   100     7273 delete $att->{"$petal:define"} || return;
    397            
    398 16         46 $expr = $class->_encode_backslash_semicolon ($expr);
    399 16         42 push @Result, map { "" } $class->_split_expression ($expr);
      23         61  
    400 16         29 return 1;
    401             }
    402              
    403              
    404             # _condition;
    405             # -----------
    406             # Rewrites statements into
    407             #
    408             sub _condition
    409             {
    410 1411     1411   1146 my $class = shift;
    411 1411 100       1591 return if ($class->_is_inside_content_or_replace());
    412              
    413 1402         1127 my $petal = $Petal::NS;
    414 1402         1027 my $tag = shift;
    415 1402         950 my $att = shift;
    416             my $expr = delete $att->{"$petal:if"} ||
    417 1402   100     5040 delete $att->{"$petal:condition"} || return;
    418            
    419 40         108 $expr = $class->_encode_backslash_semicolon ($expr);
    420 40         94 my @new = map { "" } $class->_split_expression ($expr);
      41         120  
    421 40         74 push @Result, @new;
    422 40         98 $NodeStack[$#NodeStack]->{condition} = scalar @new;
    423 40         60 return 1;
    424             }
    425              
    426              
    427             # _define_slot;
    428             # -----------
    429             # Rewrites statements into
    430             #
    431             sub _define_slot
    432             {
    433 1411     1411   1062 my $class = shift;
    434 1411 100       1674 return if ($class->_is_inside_content_or_replace());
    435              
    436 1402         1092 my $metal = $Petal::MT_NS;
    437 1402         1103 my $tag = shift;
    438 1402         985 my $att = shift;
    439 1402   100     2605 my $expr = delete $att->{"$metal:define-slot"} || return;
    440            
    441 4         13 $expr = $class->_encode_backslash_semicolon ($expr);
    442 4         13 my @new = map { "" } $class->_split_expression ($expr);
      4         12  
    443 4         7 push @Result, @new;
    444 4         10 $NodeStack[$#NodeStack]->{define_slot} = 2 * scalar @new;
    445 4         6 return 1;
    446             }
    447              
    448              
    449             # _repeat;
    450             # --------
    451             # Rewrites statements into
    452             #
    453             sub _repeat
    454             {
    455 1411     1411   1182 my $class = shift;
    456 1411 100       1644 return if ($class->_is_inside_content_or_replace());
    457              
    458 1402         1154 my $petal = $Petal::NS;
    459 1402         1114 my $tag = shift;
    460 1402         1007 my $att = shift;
    461             my $expr = delete $att->{"$petal:for"} ||
    462             delete $att->{"$petal:foreach"} ||
    463             delete $att->{"$petal:loop"} ||
    464 1402   100     9657 delete $att->{"$petal:repeat"} || return;
    465            
    466 30         85 my @exprs = $class->_split_expression ($expr);
    467 30         46 my @new = ();
    468 30         48 foreach $expr (@exprs)
    469             {
    470 30         73 $expr = $class->_encode_backslash_semicolon ($expr);
    471 30         96 push @new, ""
    472             }
    473 30         47 push @Result, @new;
    474 30         67 $NodeStack[$#NodeStack]->{repeat} = scalar @new;
    475 30         54 return 1;
    476             }
    477              
    478              
    479             # _replace;
    480             # ---------
    481             # Rewrites as
    482             # All the descendent nodes of 'tag' will be skipped
    483             sub _replace
    484             {
    485 1364     1364   1043 my $class = shift;
    486 1364 100       1574 return if ($class->_is_inside_content_or_replace());
    487            
    488 1355         1156 my $petal = $Petal::NS;
    489 1355         1112 my $tag = shift;
    490 1355         946 my $att = shift;
    491             my $expr = delete $att->{"$petal:replace"} ||
    492 1355   100     4891 delete $att->{"$petal:outer"} || return;
    493            
    494             my @new = map {
    495 29         71 $_ = $class->_encode_backslash_semicolon ($_);
      29         69  
    496 29         92 "";
    497             } $class->_split_expression ($expr);
    498            
    499 29         41 push @Result, @new;
    500 29         76 $NodeStack[$#NodeStack]->{replace} = 'true';
    501 29         47 return 1;
    502             }
    503              
    504              
    505             # _use_macro;
    506             # -----------
    507             # Rewrites
    508             # All the descendent nodes of 'tag' will be skipped
    509             sub _use_macro
    510             {
    511 1411     1411   1165 my $class = shift;
    512 1411 50       1795 return if ($class->_is_inside_content_or_replace());
    513            
    514 1411         1186 my $metal = $Petal::MT_NS;
    515            
    516 1411         1217 my $tag = shift;
    517 1411         1013 my $att = shift;
    518 1411   100     3014 my $expr = delete $att->{"$metal:use-macro"} || return;
    519            
    520 9         23 push @Result, qq||;
    521 9         19 $NodeStack[$#NodeStack]->{'use-macro'} = 'true';
    522 9         17 return 1;
    523             }
    524              
    525              
    526             # _attributes;
    527             # ------------
    528             # Rewrites
    529             # as
    530             sub _attributes
    531             {
    532 1326     1326   1008 my $class = shift;
    533 1326 50       1590 return if ($class->_is_inside_content_or_replace());
    534            
    535 1326         1195 my $petal = $Petal::NS;
    536 1326         1095 my $tag = shift;
    537 1326         929 my $att = shift;
    538             my $expr = delete $att->{"$petal:att"} ||
    539             delete $att->{"$petal:attr"} ||
    540             delete $att->{"$petal:atts"} ||
    541 1326   100     9243 delete $att->{"$petal:attributes"} || return;
    542            
    543 37         96 foreach my $string ($class->_split_expression ($expr))
    544             {
    545 53 50       97 next unless (defined $string);
    546 53 50       147 next if ($string =~ /^\s*$/);
    547 53         280 my ($attr, $expr) = $string =~ /^\s*([A-Za-z_:][A-Za-z0-9_:.-]*)\s+(.*?)\s*$/;
    548 53 50 33     177 if (not defined $attr or not defined $expr)
    549             {
    550 0         0 warn "Attributes expression '$string' does not seem valid - Skipped";
    551 0         0 next;
    552             }
    553            
    554 53         94 $expr = $class->_encode_backslash_semicolon ($expr);
    555 53         188 $att->{$attr} = "";
    556             }
    557              
    558 37         51 return 1;
    559             }
    560              
    561              
    562             # _content;
    563             # ---------
    564             # Rewrites as
    565             # All the descendent nodes of 'tag' will be skipped
    566             sub _content
    567             {
    568 1326     1326   1042 my $class = shift;
    569 1326 50       1601 return if ($class->_is_inside_content_or_replace());
    570            
    571 1326         1096 my $petal = $Petal::NS;
    572 1326         1029 my $tag = shift;
    573 1326         959 my $att = shift;
    574             my $expr = delete $att->{"$petal:content"} ||
    575             delete $att->{"$petal:contents"} ||
    576 1326   100     10105 delete $att->{"$petal:inner"} || return;
    577             my @new = map {
    578 64         148 $_ = $class->_encode_backslash_semicolon ($_);
      64         136  
    579 64         199 "";
    580             } $class->_split_expression ($expr);
    581 64         99 push @Result, @new;
    582 64         150 $NodeStack[$#NodeStack]->{content} = 'true';
    583 64         253 return 1;
    584             }
    585              
    586              
    587             # _xinclude ($tag, $att);
    588             # -----------------------
    589             # Rewrites into
    590             # .
    591             sub _xinclude
    592             {
    593 47     47   48 my $class = shift;
    594 47 50       79 return if ($class->_is_inside_content_or_replace());
    595            
    596 47         137 my $tag = shift;
    597 47         39 my $att = shift;
    598            
    599 47 50       74 if ($class->_is_xinclude ($tag))
    600             {
    601             # strip remaining Petal tags
    602 47         51 my $petal = quotemeta ($Petal::NS);
    603 47 50       47 $att = { map { $_ =~ /^$petal:/ ? () : $_ => $att->{$_} } keys %{$att} };
      47         299  
      47         117  
    604            
    605 47         80 my $expr = delete $att->{'href'};
    606 47         89 $expr = $class->_encode_backslash_semicolon ($expr);
    607 47         163 push @Result, "";
    608             }
    609 47         263 return 1;
    610             }
    611              
    612              
    613             # _is_xinclude ($tag);
    614             # --------------------
    615             # Returns TRUE if $tag is a Xinclude directive,
    616             # FALSE otherwise.
    617             sub _is_xinclude
    618             {
    619 2860     2860   2258 my $class = shift;
    620 2860         2002 my $tag = shift;
    621 2860         2282 my $xi = quotemeta ($Petal::XI_NS);
    622 2860         10370 return $tag =~ /^$xi:/
    623             }
    624              
    625              
    626             sub _encode_backslash_semicolon
    627             {
    628 354     354   333 my $class = shift;
    629 354         290 my $data = shift;
    630 354         2022 $data =~ s/($MKDoc::XML::Encode::XML_Encode_Pattern)/&$MKDoc::XML::Encode::XML_Encode{$1}\\;/go;
    631 354         521 return $data;
    632             }
    633              
    634              
    635             1;
    636              
    637              
    638             __END__