File Coverage

lib/Petal/Canonicalizer/XML.pm
Criterion Covered Total %
statement 290 295 98.3
branch 70 80 87.5
condition 50 58 86.2
subroutine 26 26 100.0
pod 0 4 0.0
total 436 463 94.1


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   527 use Petal::Hash::String;
  77         166  
  77         2323  
19 77     77   31788 use MKDoc::XML::Encode;
  77         17849  
  77         2070  
20 77     77   488 use strict;
  77         142  
  77         1403  
21 77     77   397 use warnings;
  77         130  
  77         1935  
22              
23 77     77   364 use vars qw /@Result @NodeStack/;
  77         129  
  77         296158  
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 783 my $class = shift;
35 206         4266 my $parser = shift;
36 206         442 my $data_ref = shift;
37 206 50       607 $data_ref = (ref $data_ref) ? $data_ref : \$data_ref;
38              
39             # grab anything that's before the first '<' tag
40 206         1767 my ($header) = $$data_ref =~ /(^.*?)<(?!\?|\!)/sm;
41 206         1968 $$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         744 my $pis = $class->_processing_instructions_out ($data_ref);
50              
51 206         475 local @Result = ();
52 206         334 local @NodeStack = ();
53              
54 206         666 $parser->process ($class, $data_ref);
55              
56 202   100     964 $header ||= '';
57 202         326 my $res = '';
58 202 100       626 $res .= $header unless ($Petal::CURRENT_INCLUDES > 1);
59 202         1760 $res .= (join '', @Result);
60              
61 202         865 $class->_processing_instructions_in (\$res, $pis);
62              
63 202         1794 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   342 my $class = shift;
82 206         314 my $data_ref = shift;
83 206         909 my %pis = map { $_ => $class->_compute_unique_string ($data_ref) } $$data_ref =~ /(<\?.*?\?>)/gsm;
  60         155  
84              
85 206         1002 while (my ($key, $value) = each %pis) {
86 49         1630 $$data_ref =~ s/\Q$key\E/$value/gsm;
87             }
88              
89 206         462 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   354 my $class = shift;
101 202         289 my $data_ref = shift;
102 202         287 my $pis = shift;
103 202         350 while (my ($key, $value) = each %{$pis}) {
  251         1106  
104 49         1606 $$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   86 my $class = shift;
115 60         71 my $data_ref = shift;
116 60         138 my $string = '[-' . (join '', map { chr (ord ('a') + int rand 26) } 1..20) . '-]';
  1200         2523  
117 60         367 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         219 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 3965 Petal::load_code_generator(); # we will use it later
141              
142 1306         1861 my $class = shift;
143 1306         2406 push @NodeStack, {};
144 1306 100       3010 return if ($class->_is_inside_content_or_replace());
145              
146 1299         1826 my $tag = $_;
147 1299         6711 ($tag) = $tag =~ /^<\s*((?:\w|\:|\-)*)/;
148 1299         5221 my $att = { %_ };
149              
150 1299         3798 $class->_use_macro ($tag, $att);
151 1299         2984 $class->_on_error ($tag, $att);
152 1299         3075 $class->_define ($tag, $att);
153 1299         3075 $class->_define_slot ($tag, $att);
154 1299         2852 $class->_condition ($tag, $att);
155 1299         3056 $class->_repeat ($tag, $att);
156 1299 100 66     2715 $class->_is_xinclude ($tag) and $class->_xinclude ($tag, $att) and return;
157 1253         3672 $class->_replace ($tag, $att);
158              
159 1253         1963 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       2022 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         1555 foreach my $key (keys %{$att})
  1224         4273  
168             {
169 2008 100       5984 next if ($key =~ /^$petal:/);
170 1922         3355 my $text = $att->{$key};
171 1922         2218 my $token_re = $Petal::Hash::String::TOKEN_RE;
172 1922         6532 my @vars = $text =~ /$token_re/gsm;
173 1922         2956 my %vars = map { $_ => 1 } @vars;
  3         27  
174 1922         2971 @vars = sort { length ($b) <=> length ($a) } keys %vars;
  0         0  
175 1922         2610 foreach my $var (@vars)
176             {
177 3         18 my $command = $var;
178 3         23 $command =~ s/^\$//;
179 3         25 $command =~ s/^\{//;
180 3         13 $command =~ s/\}$//;
181 3         16 $command = $class->_encode_backslash_semicolon ($command);
182 3         20 $command = "";
183 3         59 $text =~ s/\Q$var\E/$command/g;
184             }
185 1922         4356 $att->{$key} = $text;
186             }
187              
188             # processes the petal:attributes instruction
189 1224         3610 $class->_attributes ($tag, $att);
190              
191 1224         2032 my @att_str = ();
192 1224         1436 foreach my $key (keys %{$att})
  1224         3062  
193             {
194 1987 100       5699 next if ($key =~ /^$petal:/);
195 1930         3458 my $value = $att->{$key};
196 1930 100       3334 if ($value =~ /^<\?attr/)
197             {
198 45         127 push @att_str, $value;
199             }
200             else
201             {
202 1885         4208 my $tokens = Petal::CodeGenerator->_tokenize (\$value);
203             my @res = map {
204             ($_ =~ /$Petal::CodeGenerator::PI_RE/s) ?
205             $_ :
206 1884 100       5061 do {
207 1881         2987 $_ =~ s/\&/&/g;
208 1881         2386 $_ =~ s/\
209 1881         2242 $_ =~ s/\>/>/g;
210 1881         2377 $_ =~ s/\"/"/g;
211 1881         4138 $_;
212             };
213 1885         2275 } @{$tokens};
  1885         2559  
214 1885         7899 push @att_str, $key . '="' . (join '', @res) . '"';
215             }
216             }
217              
218 1224         3186 my $att_str = join " ", @att_str;
219              
220 1224 100       3086 if (defined $att->{"$petal:omit-tag"})
221             {
222 5   100     23 my $expression = $att->{"$petal:omit-tag"} || 'string:1';
223 5         16 $NodeStack[$#NodeStack]->{'omit-tag'} = $expression;
224 5 100 66     60 push @Result, (defined $att_str and $att_str) ?
225             "<$tag $att_str>" :
226             "<$tag>";
227             }
228             else
229             {
230 1219 100 66     6380 push @Result, (defined $att_str and $att_str) ? "<$tag $att_str>" : "<$tag>";
231             }
232              
233 1224         3254 $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 1761 my $class = shift;
    254 1306 100       2152 return if ($class->_is_inside_content_or_replace ( 'endtag' ));
    255              
    256 1294         6489 my ($tag) = $_ =~ /^<\/\s*((?:\w|\:|\-)*)/;
    257 1294         2329 my $node = pop (@NodeStack);
    258              
    259 1294 100       2623 return if ($class->_is_xinclude ($tag));
    260              
    261 1248 100 66     3364 unless (defined $node->{replace} and $node->{replace})
    262             {
    263 1224 100       2321 if (exists $node->{'omit-tag'})
    264             {
    265 5         13 my $expression = $node->{'omit-tag'};
    266 5         22 push @Result, "";
    267             }
    268             else
    269             {
    270 1219         3362 push @Result, "";
    271             }
    272             }
    273              
    274 1248   100     3810 my $repeat = $node->{repeat} || '0';
    275 1248   100     3168 my $condition = $node->{condition} || '0';
    276 1248   100     2938 my $define_slot = $node->{define_slot} || '0';
    277 1248         2783 push @Result, map { '' } 1 .. ($repeat+$condition+$define_slot);
      58         157  
    278              
    279 1248 100 66     3101 unless (defined $node->{replace} and $node->{replace})
    280             {
    281 1224 100       3585 if (exists $node->{'on-error'})
    282             {
    283 4         8 my $expression = $node->{'on-error'};
    284 4         23 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 2877 my $class = shift;
    297 2118 100       3526 return if ($class->_is_inside_content_or_replace());
    298 2017         2893 my $text = $_;
    299 2017         2700 my $token_re = $Petal::Hash::String::TOKEN_RE;
    300 2017         9713 my @vars = $text =~ /$token_re/gsm;
    301 2017         3355 my %vars = map { $_ => 1 } @vars;
      64         272  
    302 2017         4020 @vars = sort { length ($b) <=> length ($a) } keys %vars;
      25         137  
    303 2017         3213 foreach my $var (@vars)
    304             {
    305 64         138 my $command = $var;
    306 64         273 $command =~ s/^\$//;
    307 64         142 $command =~ s/^\{//;
    308 64         125 $command =~ s/\}$//;
    309 64         160 $command = $class->_encode_backslash_semicolon ($command);
    310 64         179 $command = "";
    311 64         923 $text =~ s/\Q$var\E/$command/g;
    312             }
    313 2017         8060 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   20925 my $class = shift;
    324 18855         19549 my $endtag = shift;
    325 18855         19801 my $tmp = undef;
    326 18855 100       27165 $tmp = pop (@NodeStack) if ($endtag);
    327              
    328             # WHY do I have to do this?
    329 18855 100 100     29658 return 1 if (defined $tmp and $tmp->{'use-macro'});
    330 18846         33564 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     251378 defined $NodeStack[$i]->{'use-macro'} );
          100        
    335             }
    336 18631 100       26957 push @NodeStack, $tmp if (defined $tmp);
    337 18631         34430 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   329 my $class = shift;
    350 220         296 my $expression = shift;
    351 220 100 66     1516 my @tokens = map { (defined $_ and $_) ? $_ : () }
      296         1480  
    352             split /(\s|\r|\n)*(?
    353             $expression;
    354              
    355 220         443 return map { s/^(\s|\n|\r)+//sm;
      270         860  
    356 270         1319 s/(\s|\n|\r)+$//sm;
    357 270 100       914 ($_ eq '') ? () : $_ } @tokens;
    358             }
    359              
    360              
    361             # _condition;
    362             # -----------
    363             # Rewrites statements into
    364             #
    365             sub _on_error
    366             {
    367 1411     1411   1713 my $class = shift;
    368 1411 100       2138 return if ($class->_is_inside_content_or_replace());
    369              
    370 1402         2165 my $petal = quotemeta ($Petal::NS);
    371 1402         1706 my $tag = shift;
    372 1402         1619 my $att = shift;
    373 1402   100     3384 my $expr = delete $att->{"$petal:on-error"} || return;
    374              
    375 4         11 $expr = $class->_encode_backslash_semicolon ($expr);
    376 4         11 push @Result, "";
    377 4         11 $NodeStack[$#NodeStack]->{'on-error'} = $expr;
    378 4         7 return 1;
    379             }
    380              
    381              
    382             # _define;
    383             # --------
    384             # Rewrites statements into
    385             # canonical
    386             sub _define
    387             {
    388 1411     1411   1750 my $class = shift;
    389 1411 100       2207 return if ($class->_is_inside_content_or_replace());
    390              
    391 1402         1818 my $petal = $Petal::NS;
    392 1402         1719 my $tag = shift;
    393 1402         1541 my $att = shift;
    394             my $expr = delete $att->{"$petal:set"} ||
    395             delete $att->{"$petal:def"} ||
    396 1402   100     7732 delete $att->{"$petal:define"} || return;
    397              
    398 16         126 $expr = $class->_encode_backslash_semicolon ($expr);
    399 16         79 push @Result, map { "" } $class->_split_expression ($expr);
      23         83  
    400 16         38 return 1;
    401             }
    402              
    403              
    404             # _condition;
    405             # -----------
    406             # Rewrites statements into
    407             #
    408             sub _condition
    409             {
    410 1411     1411   1786 my $class = shift;
    411 1411 100       2136 return if ($class->_is_inside_content_or_replace());
    412              
    413 1402         1845 my $petal = $Petal::NS;
    414 1402         1750 my $tag = shift;
    415 1402         1705 my $att = shift;
    416             my $expr = delete $att->{"$petal:if"} ||
    417 1402   100     5573 delete $att->{"$petal:condition"} || return;
    418              
    419 40         149 $expr = $class->_encode_backslash_semicolon ($expr);
    420 40         123 my @new = map { "" } $class->_split_expression ($expr);
      41         186  
    421 40         99 push @Result, @new;
    422 40         163 $NodeStack[$#NodeStack]->{condition} = scalar @new;
    423 40         113 return 1;
    424             }
    425              
    426              
    427             # _define_slot;
    428             # -----------
    429             # Rewrites statements into
    430             #
    431             sub _define_slot
    432             {
    433 1411     1411   1724 my $class = shift;
    434 1411 100       2279 return if ($class->_is_inside_content_or_replace());
    435              
    436 1402         1773 my $metal = $Petal::MT_NS;
    437 1402         1814 my $tag = shift;
    438 1402         1659 my $att = shift;
    439 1402   100     3275 my $expr = delete $att->{"$metal:define-slot"} || return;
    440              
    441 4         17 $expr = $class->_encode_backslash_semicolon ($expr);
    442 4         14 my @new = map { "" } $class->_split_expression ($expr);
      4         17  
    443 4         10 push @Result, @new;
    444 4         16 $NodeStack[$#NodeStack]->{define_slot} = 2 * scalar @new;
    445 4         9 return 1;
    446             }
    447              
    448              
    449             # _repeat;
    450             # --------
    451             # Rewrites statements into
    452             #
    453             sub _repeat
    454             {
    455 1411     1411   1740 my $class = shift;
    456 1411 100       2300 return if ($class->_is_inside_content_or_replace());
    457              
    458 1402         1735 my $petal = $Petal::NS;
    459 1402         1744 my $tag = shift;
    460 1402         1655 my $att = shift;
    461             my $expr = delete $att->{"$petal:for"} ||
    462             delete $att->{"$petal:foreach"} ||
    463             delete $att->{"$petal:loop"} ||
    464 1402   100     10216 delete $att->{"$petal:repeat"} || return;
    465              
    466 30         117 my @exprs = $class->_split_expression ($expr);
    467 30         68 my @new = ();
    468 30         78 foreach $expr (@exprs)
    469             {
    470 30         90 $expr = $class->_encode_backslash_semicolon ($expr);
    471 30         129 push @new, ""
    472             }
    473 30         66 push @Result, @new;
    474 30         100 $NodeStack[$#NodeStack]->{repeat} = scalar @new;
    475 30         71 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   1688 my $class = shift;
    486 1364 100       2289 return if ($class->_is_inside_content_or_replace());
    487              
    488 1355         1808 my $petal = $Petal::NS;
    489 1355         1698 my $tag = shift;
    490 1355         1559 my $att = shift;
    491             my $expr = delete $att->{"$petal:replace"} ||
    492 1355   100     5694 delete $att->{"$petal:outer"} || return;
    493              
    494             my @new = map {
    495 29         118 $_ = $class->_encode_backslash_semicolon ($_);
      29         116  
    496 29         122 "";
    497             } $class->_split_expression ($expr);
    498              
    499 29         89 push @Result, @new;
    500 29         87 $NodeStack[$#NodeStack]->{replace} = 'true';
    501 29         70 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   1929 my $class = shift;
    512 1411 50       2231 return if ($class->_is_inside_content_or_replace());
    513              
    514 1411         1815 my $metal = $Petal::MT_NS;
    515              
    516 1411         1830 my $tag = shift;
    517 1411         1568 my $att = shift;
    518 1411   100     3933 my $expr = delete $att->{"$metal:use-macro"} || return;
    519              
    520 9         26 push @Result, qq||;
    521 9         44 $NodeStack[$#NodeStack]->{'use-macro'} = 'true';
    522 9         18 return 1;
    523             }
    524              
    525              
    526             # _attributes;
    527             # ------------
    528             # Rewrites
    529             # as
    530             sub _attributes
    531             {
    532 1326     1326   1812 my $class = shift;
    533 1326 50       2097 return if ($class->_is_inside_content_or_replace());
    534              
    535 1326         1744 my $petal = $Petal::NS;
    536 1326         1722 my $tag = shift;
    537 1326         1559 my $att = shift;
    538             my $expr = delete $att->{"$petal:att"} ||
    539             delete $att->{"$petal:attr"} ||
    540             delete $att->{"$petal:atts"} ||
    541 1326   100     10305 delete $att->{"$petal:attributes"} || return;
    542              
    543 37         166 foreach my $string ($class->_split_expression ($expr))
    544             {
    545 53 50       137 next unless (defined $string);
    546 53 50       242 next if ($string =~ /^\s*$/);
    547 53         479 my ($attr, $expr) = $string =~ /^\s*([A-Za-z_:][A-Za-z0-9_:.-]*)\s+(.*?)\s*$/;
    548 53 50 33     240 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         153 $expr = $class->_encode_backslash_semicolon ($expr);
    555 53         285 $att->{$attr} = "";
    556             }
    557              
    558 37         77 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   1818 my $class = shift;
    569 1326 50       2401 return if ($class->_is_inside_content_or_replace());
    570              
    571 1326         1763 my $petal = $Petal::NS;
    572 1326         1738 my $tag = shift;
    573 1326         1641 my $att = shift;
    574             my $expr = delete $att->{"$petal:content"} ||
    575             delete $att->{"$petal:contents"} ||
    576 1326   100     11638 delete $att->{"$petal:inner"} || return;
    577             my @new = map {
    578 64         237 $_ = $class->_encode_backslash_semicolon ($_);
      64         192  
    579 64         282 "";
    580             } $class->_split_expression ($expr);
    581 64         173 push @Result, @new;
    582 64         237 $NodeStack[$#NodeStack]->{content} = 'true';
    583 64         337 return 1;
    584             }
    585              
    586              
    587             # _xinclude ($tag, $att);
    588             # -----------------------
    589             # Rewrites into
    590             # .
    591             sub _xinclude
    592             {
    593 47     47   76 my $class = shift;
    594 47 50       91 return if ($class->_is_inside_content_or_replace());
    595              
    596 47         80 my $tag = shift;
    597 47         67 my $att = shift;
    598              
    599 47 50       89 if ($class->_is_xinclude ($tag))
    600             {
    601             # strip remaining Petal tags
    602 47         93 my $petal = quotemeta ($Petal::NS);
    603 47 50       69 $att = { map { $_ =~ /^$petal:/ ? () : $_ => $att->{$_} } keys %{$att} };
      47         388  
      47         210  
    604              
    605 47         119 my $expr = delete $att->{'href'};
    606 47         111 $expr = $class->_encode_backslash_semicolon ($expr);
    607 47         184 push @Result, "";
    608             }
    609 47         295 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   3817 my $class = shift;
    620 2860         3279 my $tag = shift;
    621 2860         3627 my $xi = quotemeta ($Petal::XI_NS);
    622 2860         13241 return $tag =~ /^$xi:/
    623             }
    624              
    625              
    626             sub _encode_backslash_semicolon
    627             {
    628 354     354   523 my $class = shift;
    629 354         472 my $data = shift;
    630 354         2879 $data =~ s/($MKDoc::XML::Encode::XML_Encode_Pattern)/&$MKDoc::XML::Encode::XML_Encode{$1}\\;/go;
    631 354         798 return $data;
    632             }
    633              
    634              
    635             1;
    636              
    637              
    638             __END__