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   480 use Petal::Hash::String;
  77         143  
  77         2200  
19 77     77   29486 use MKDoc::XML::Encode;
  77         18215  
  77         1976  
20 77     77   436 use strict;
  77         137  
  77         1268  
21 77     77   335 use warnings;
  77         118  
  77         2239  
22              
23 77     77   416 use vars qw /@Result @NodeStack/;
  77         120  
  77         279050  
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 375 my $class = shift;
35 206         393 my $parser = shift;
36 206         324 my $data_ref = shift;
37 206 50       889 $data_ref = (ref $data_ref) ? $data_ref : \$data_ref;
38            
39             # grab anything that's before the first '<' tag
40 206         5418 my ($header) = $$data_ref =~ /(^.*?)<(?!\?|\!)/sm;
41 206         2112 $$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         760 my $pis = $class->_processing_instructions_out ($data_ref);
50            
51 206         458 local @Result = ();
52 206         344 local @NodeStack = ();
53            
54 206         633 $parser->process ($class, $data_ref);
55            
56 202   100     906 $header ||= '';
57 202         319 my $res = '';
58 202 100       593 $res .= $header unless ($Petal::CURRENT_INCLUDES > 1);
59 202         1678 $res .= (join '', @Result);
60              
61 202         889 $class->_processing_instructions_in (\$res, $pis);
62            
63 202         1718 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   354 my $class = shift;
82 206         276 my $data_ref = shift;
83 206         878 my %pis = map { $_ => $class->_compute_unique_string ($data_ref) } $$data_ref =~ /(<\?.*?\?>)/gsm;
  60         146  
84            
85 206         949 while (my ($key, $value) = each %pis) {
86 49         1645 $$data_ref =~ s/\Q$key\E/$value/gsm;
87             }
88            
89 206         501 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   350 my $class = shift;
101 202         312 my $data_ref = shift;
102 202         297 my $pis = shift;
103 202         342 while (my ($key, $value) = each %{$pis}) {
  251         1035  
104 49         1505 $$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         63 my $data_ref = shift;
116 60         108 my $string = '[-' . (join '', map { chr (ord ('a') + int rand 26) } 1..20) . '-]';
  1200         2376  
117 60         352 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         209 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 3651 Petal::load_code_generator(); # we will use it later
141            
142 1306         1804 my $class = shift;
143 1306         2351 push @NodeStack, {};
144 1306 100       2886 return if ($class->_is_inside_content_or_replace());
145            
146 1299         1851 my $tag = $_;
147 1299         6560 ($tag) = $tag =~ /^<\s*((?:\w|\:|\-)*)/;
148 1299         5116 my $att = { %_ };
149            
150 1299         3868 $class->_use_macro ($tag, $att);
151 1299         2981 $class->_on_error ($tag, $att);
152 1299         2825 $class->_define ($tag, $att);
153 1299         3091 $class->_define_slot ($tag, $att);
154 1299         2745 $class->_condition ($tag, $att);
155 1299         2891 $class->_repeat ($tag, $att);
156 1299 100 66     2907 $class->_is_xinclude ($tag) and $class->_xinclude ($tag, $att) and return;
157 1253         3294 $class->_replace ($tag, $att);
158            
159 1253         1894 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       2159 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         1402 foreach my $key (keys %{$att})
  1224         4362  
168             {
169 2008 100       5930 next if ($key =~ /^$petal:/);
170 1922         3283 my $text = $att->{$key};
171 1922         2246 my $token_re = $Petal::Hash::String::TOKEN_RE;
172 1922         6307 my @vars = $text =~ /$token_re/gsm;
173 1922         2789 my %vars = map { $_ => 1 } @vars;
  3         14  
174 1922         2698 @vars = sort { length ($b) <=> length ($a) } keys %vars;
  0         0  
175 1922         2962 foreach my $var (@vars)
176             {
177 3         6 my $command = $var;
178 3         27 $command =~ s/^\$//;
179 3         14 $command =~ s/^\{//;
180 3         19 $command =~ s/\}$//;
181 3         20 $command = $class->_encode_backslash_semicolon ($command);
182 3         12 $command = "";
183 3         40 $text =~ s/\Q$var\E/$command/g;
184             }
185 1922         4478 $att->{$key} = $text;
186             }
187              
188             # processes the petal:attributes instruction
189 1224         3371 $class->_attributes ($tag, $att);
190            
191 1224         1964 my @att_str = ();
192 1224         1366 foreach my $key (keys %{$att})
  1224         2775  
193             {
194 1987 100       5630 next if ($key =~ /^$petal:/);
195 1930         3400 my $value = $att->{$key};
196 1930 100       3317 if ($value =~ /^<\?attr/)
197             {
198 45         129 push @att_str, $value;
199             }
200             else
201             {
202 1885         4514 my $tokens = Petal::CodeGenerator->_tokenize (\$value);
203             my @res = map {
204             ($_ =~ /$Petal::CodeGenerator::PI_RE/s) ?
205             $_ :
206 1884 100       5060 do {
207 1881         3188 $_ =~ s/\&/&/g;
208 1881         2348 $_ =~ s/\
209 1881         2218 $_ =~ s/\>/>/g;
210 1881         2447 $_ =~ s/\"/"/g;
211 1881         4110 $_;
212             };
213 1885         2239 } @{$tokens};
  1885         2460  
214 1885         7893 push @att_str, $key . '="' . (join '', @res) . '"';
215             }
216             }
217            
218 1224         2837 my $att_str = join " ", @att_str;
219              
220 1224 100       2989 if (defined $att->{"$petal:omit-tag"})
221             {
222 5   100     25 my $expression = $att->{"$petal:omit-tag"} || 'string:1';
223 5         27 $NodeStack[$#NodeStack]->{'omit-tag'} = $expression;
224 5 100 66     46 push @Result, (defined $att_str and $att_str) ?
225             "<$tag $att_str>" :
226             "<$tag>";
227             }
228             else
229             {
230 1219 100 66     6110 push @Result, (defined $att_str and $att_str) ? "<$tag $att_str>" : "<$tag>";
231             }
232            
233 1224         3107 $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 1680 my $class = shift;
    254 1306 100       2181 return if ($class->_is_inside_content_or_replace ( 'endtag' ));
    255            
    256 1294         6288 my ($tag) = $_ =~ /^<\/\s*((?:\w|\:|\-)*)/;
    257 1294         2158 my $node = pop (@NodeStack);
    258            
    259 1294 100       2515 return if ($class->_is_xinclude ($tag));
    260            
    261 1248 100 66     3163 unless (defined $node->{replace} and $node->{replace})
    262             {
    263 1224 100       2231 if (exists $node->{'omit-tag'})
    264             {
    265 5         9 my $expression = $node->{'omit-tag'};
    266 5         24 push @Result, "";
    267             }
    268             else
    269             {
    270 1219         3197 push @Result, "";
    271             }
    272             }
    273            
    274 1248   100     3737 my $repeat = $node->{repeat} || '0';
    275 1248   100     3380 my $condition = $node->{condition} || '0';
    276 1248   100     3091 my $define_slot = $node->{define_slot} || '0';
    277 1248         2840 push @Result, map { '' } 1 .. ($repeat+$condition+$define_slot);
      58         168  
    278              
    279 1248 100 66     3210 unless (defined $node->{replace} and $node->{replace})
    280             {
    281 1224 100       3347 if (exists $node->{'on-error'})
    282             {
    283 4         6 my $expression = $node->{'on-error'};
    284 4         21 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 2757 my $class = shift;
    297 2118 100       3331 return if ($class->_is_inside_content_or_replace());
    298 2017         2733 my $text = $_;
    299 2017         2591 my $token_re = $Petal::Hash::String::TOKEN_RE;
    300 2017         9015 my @vars = $text =~ /$token_re/gsm;
    301 2017         3180 my %vars = map { $_ => 1 } @vars;
      64         205  
    302 2017         4020 @vars = sort { length ($b) <=> length ($a) } keys %vars;
      28         89  
    303 2017         3118 foreach my $var (@vars)
    304             {
    305 64         99 my $command = $var;
    306 64         249 $command =~ s/^\$//;
    307 64         150 $command =~ s/^\{//;
    308 64         122 $command =~ s/\}$//;
    309 64         171 $command = $class->_encode_backslash_semicolon ($command);
    310 64         176 $command = "";
    311 64         923 $text =~ s/\Q$var\E/$command/g;
    312             }
    313 2017         7622 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   20209 my $class = shift;
    324 18855         19401 my $endtag = shift;
    325 18855         19580 my $tmp = undef;
    326 18855 100       27360 $tmp = pop (@NodeStack) if ($endtag);
    327            
    328             # WHY do I have to do this?
    329 18855 100 100     29189 return 1 if (defined $tmp and $tmp->{'use-macro'});
    330 18846         32841 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     246919 defined $NodeStack[$i]->{'use-macro'} );
          100        
    335             }
    336 18631 100       27282 push @NodeStack, $tmp if (defined $tmp);
    337 18631         33691 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   334 my $class = shift;
    350 220         285 my $expression = shift;
    351 220 100 66     1134 my @tokens = map { (defined $_ and $_) ? $_ : () }
      296         1493  
    352             split /(\s|\r|\n)*(?
    353             $expression;
    354            
    355 220         387 return map { s/^(\s|\n|\r)+//sm;
      270         780  
    356 270         1260 s/(\s|\n|\r)+$//sm;
    357 270 100       904 ($_ eq '') ? () : $_ } @tokens;
    358             }
    359              
    360              
    361             # _condition;
    362             # -----------
    363             # Rewrites statements into
    364             #
    365             sub _on_error
    366             {
    367 1411     1411   1724 my $class = shift;
    368 1411 100       2214 return if ($class->_is_inside_content_or_replace());
    369            
    370 1402         2097 my $petal = quotemeta ($Petal::NS);
    371 1402         1672 my $tag = shift;
    372 1402         1622 my $att = shift;
    373 1402   100     3256 my $expr = delete $att->{"$petal:on-error"} || return;
    374            
    375 4         12 $expr = $class->_encode_backslash_semicolon ($expr);
    376 4         11 push @Result, "";
    377 4         12 $NodeStack[$#NodeStack]->{'on-error'} = $expr;
    378 4         8 return 1;
    379             }
    380              
    381              
    382             # _define;
    383             # --------
    384             # Rewrites statements into
    385             # canonical
    386             sub _define
    387             {
    388 1411     1411   1650 my $class = shift;
    389 1411 100       2482 return if ($class->_is_inside_content_or_replace());
    390            
    391 1402         1750 my $petal = $Petal::NS;
    392 1402         1703 my $tag = shift;
    393 1402         1522 my $att = shift;
    394             my $expr = delete $att->{"$petal:set"} ||
    395             delete $att->{"$petal:def"} ||
    396 1402   100     7462 delete $att->{"$petal:define"} || return;
    397            
    398 16         58 $expr = $class->_encode_backslash_semicolon ($expr);
    399 16         59 push @Result, map { "" } $class->_split_expression ($expr);
      23         92  
    400 16         33 return 1;
    401             }
    402              
    403              
    404             # _condition;
    405             # -----------
    406             # Rewrites statements into
    407             #
    408             sub _condition
    409             {
    410 1411     1411   1668 my $class = shift;
    411 1411 100       2278 return if ($class->_is_inside_content_or_replace());
    412              
    413 1402         1836 my $petal = $Petal::NS;
    414 1402         1835 my $tag = shift;
    415 1402         1576 my $att = shift;
    416             my $expr = delete $att->{"$petal:if"} ||
    417 1402   100     5811 delete $att->{"$petal:condition"} || return;
    418            
    419 40         144 $expr = $class->_encode_backslash_semicolon ($expr);
    420 40         112 my @new = map { "" } $class->_split_expression ($expr);
      41         186  
    421 40         103 push @Result, @new;
    422 40         132 $NodeStack[$#NodeStack]->{condition} = scalar @new;
    423 40         108 return 1;
    424             }
    425              
    426              
    427             # _define_slot;
    428             # -----------
    429             # Rewrites statements into
    430             #
    431             sub _define_slot
    432             {
    433 1411     1411   1776 my $class = shift;
    434 1411 100       2116 return if ($class->_is_inside_content_or_replace());
    435              
    436 1402         1896 my $metal = $Petal::MT_NS;
    437 1402         1771 my $tag = shift;
    438 1402         1525 my $att = shift;
    439 1402   100     3381 my $expr = delete $att->{"$metal:define-slot"} || return;
    440            
    441 4         12 $expr = $class->_encode_backslash_semicolon ($expr);
    442 4         24 my @new = map { "" } $class->_split_expression ($expr);
      4         17  
    443 4         9 push @Result, @new;
    444 4         15 $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   1893 my $class = shift;
    456 1411 100       2129 return if ($class->_is_inside_content_or_replace());
    457              
    458 1402         1861 my $petal = $Petal::NS;
    459 1402         1709 my $tag = shift;
    460 1402         1533 my $att = shift;
    461             my $expr = delete $att->{"$petal:for"} ||
    462             delete $att->{"$petal:foreach"} ||
    463             delete $att->{"$petal:loop"} ||
    464 1402   100     9980 delete $att->{"$petal:repeat"} || return;
    465            
    466 30         115 my @exprs = $class->_split_expression ($expr);
    467 30         63 my @new = ();
    468 30         56 foreach $expr (@exprs)
    469             {
    470 30         83 $expr = $class->_encode_backslash_semicolon ($expr);
    471 30         141 push @new, ""
    472             }
    473 30         68 push @Result, @new;
    474 30         93 $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   1776 my $class = shift;
    486 1364 100       2142 return if ($class->_is_inside_content_or_replace());
    487            
    488 1355         1922 my $petal = $Petal::NS;
    489 1355         1747 my $tag = shift;
    490 1355         1495 my $att = shift;
    491             my $expr = delete $att->{"$petal:replace"} ||
    492 1355   100     5507 delete $att->{"$petal:outer"} || return;
    493            
    494             my @new = map {
    495 29         102 $_ = $class->_encode_backslash_semicolon ($_);
      29         100  
    496 29         149 "";
    497             } $class->_split_expression ($expr);
    498            
    499 29         76 push @Result, @new;
    500 29         102 $NodeStack[$#NodeStack]->{replace} = 'true';
    501 29         61 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   1750 my $class = shift;
    512 1411 50       2431 return if ($class->_is_inside_content_or_replace());
    513            
    514 1411         1709 my $metal = $Petal::MT_NS;
    515            
    516 1411         1832 my $tag = shift;
    517 1411         1692 my $att = shift;
    518 1411   100     3820 my $expr = delete $att->{"$metal:use-macro"} || return;
    519            
    520 9         33 push @Result, qq||;
    521 9         35 $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   1811 my $class = shift;
    533 1326 50       2243 return if ($class->_is_inside_content_or_replace());
    534            
    535 1326         1882 my $petal = $Petal::NS;
    536 1326         1642 my $tag = shift;
    537 1326         1585 my $att = shift;
    538             my $expr = delete $att->{"$petal:att"} ||
    539             delete $att->{"$petal:attr"} ||
    540             delete $att->{"$petal:atts"} ||
    541 1326   100     9537 delete $att->{"$petal:attributes"} || return;
    542            
    543 37         140 foreach my $string ($class->_split_expression ($expr))
    544             {
    545 53 50       134 next unless (defined $string);
    546 53 50       193 next if ($string =~ /^\s*$/);
    547 53         393 my ($attr, $expr) = $string =~ /^\s*([A-Za-z_:][A-Za-z0-9_:.-]*)\s+(.*?)\s*$/;
    548 53 50 33     226 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         130 $expr = $class->_encode_backslash_semicolon ($expr);
    555 53         241 $att->{$attr} = "";
    556             }
    557              
    558 37         82 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   1978 my $class = shift;
    569 1326 50       2188 return if ($class->_is_inside_content_or_replace());
    570            
    571 1326         1850 my $petal = $Petal::NS;
    572 1326         1731 my $tag = shift;
    573 1326         1541 my $att = shift;
    574             my $expr = delete $att->{"$petal:content"} ||
    575             delete $att->{"$petal:contents"} ||
    576 1326   100     11462 delete $att->{"$petal:inner"} || return;
    577             my @new = map {
    578 64         204 $_ = $class->_encode_backslash_semicolon ($_);
      64         172  
    579 64         251 "";
    580             } $class->_split_expression ($expr);
    581 64         481 push @Result, @new;
    582 64         195 $NodeStack[$#NodeStack]->{content} = 'true';
    583 64         323 return 1;
    584             }
    585              
    586              
    587             # _xinclude ($tag, $att);
    588             # -----------------------
    589             # Rewrites into
    590             # .
    591             sub _xinclude
    592             {
    593 47     47   88 my $class = shift;
    594 47 50       85 return if ($class->_is_inside_content_or_replace());
    595            
    596 47         75 my $tag = shift;
    597 47         68 my $att = shift;
    598            
    599 47 50       82 if ($class->_is_xinclude ($tag))
    600             {
    601             # strip remaining Petal tags
    602 47         89 my $petal = quotemeta ($Petal::NS);
    603 47 50       60 $att = { map { $_ =~ /^$petal:/ ? () : $_ => $att->{$_} } keys %{$att} };
      47         381  
      47         148  
    604            
    605 47         125 my $expr = delete $att->{'href'};
    606 47         122 $expr = $class->_encode_backslash_semicolon ($expr);
    607 47         141 push @Result, "";
    608             }
    609 47         299 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   3510 my $class = shift;
    620 2860         3293 my $tag = shift;
    621 2860         3496 my $xi = quotemeta ($Petal::XI_NS);
    622 2860         12952 return $tag =~ /^$xi:/
    623             }
    624              
    625              
    626             sub _encode_backslash_semicolon
    627             {
    628 354     354   496 my $class = shift;
    629 354         515 my $data = shift;
    630 354         2626 $data =~ s/($MKDoc::XML::Encode::XML_Encode_Pattern)/&$MKDoc::XML::Encode::XML_Encode{$1}\\;/go;
    631 354         763 return $data;
    632             }
    633              
    634              
    635             1;
    636              
    637              
    638             __END__