File Coverage

blib/lib/XML/Smart/DTD.pm
Criterion Covered Total %
statement 333 427 77.9
branch 142 236 60.1
condition 52 119 43.7
subroutine 33 42 78.5
pod 29 38 76.3
total 589 862 68.3


line stmt bran cond sub pod time code
1             #############################################################################
2             ## Name: DTD.pm
3             ## Purpose: XML::Smart::DTD - Apply DTD over a XML::Smart object.
4             ## Author: Graciliano M. P.
5             ## Modified by: Harish Madabushi
6             ## Created: 25/05/2004
7             ## RCS-ID:
8             ##
9             ## The DTD parser was based on XML-DTDParser-1.7
10             ## by Jenda@Krynicky.cz http://Jenda.Krynicky.cz
11             ##
12             ## Copyright: (c) 2004 Graciliano M. P.
13             ## Licence: This program is free software; you can redistribute it and/or
14             ## modify it under the same terms as Perl itself
15             #############################################################################
16            
17             package XML::Smart::DTD ;
18            
19 4     4   55121 use strict ;
  4         11  
  4         176  
20 4     4   24 use warnings ;
  4         18  
  4         167  
21            
22 4     4   25 use XML::Smart::Shared qw( _unset_sig_warn _reset_sig_warn ) ;
  4         8  
  4         26763  
23            
24             our ($VERSION , @ISA) ;
25             $VERSION = '0.05' ;
26            
27             ########
28             # VARS #
29             ########
30            
31             my $RE_quoted = qr/(?:"[^"\\]?"|"(?:(?:\\")|[^"])+(?!\\)[^"]?"|'[^'\\]?'|'(?:(?:\\')|[^'])+(?!\\)[^']')/s ;
32            
33             my $namechar = qr/[#\x41-\x5A\x61-\x7A\xC0-\xD6\xD8-\xF6\xF8-\xFF0-9\xB7._:-]/;
34             my $name = qr/[\x41-\x5A\x61-\x7A\xC0-\xD6\xD8-\xF6\xF8-\xFF_:]$namechar*/ ;
35             my $nameX = qr/$name[.?+*]*/ ;
36            
37             my $nmtoken = qr/$namechar+/ ;
38            
39             my $AttType = qr/(?:CDATA|ID|IDREF|IDREFS|ENTITY|ENTITIES|NMTOKEN|NMTOKENS|\(.*?\)|NOTATION ?\(.*?\))/ ;
40             my $DefaultDecl = qr/(?:#REQUIRED|#IMPLIED|#FIXED)/ ;
41             my $AttDef = qr/($name)[ \t]+($AttType)(?:[ \t]+($DefaultDecl))?(?:[ \t]+($RE_quoted))?/ ;
42            
43             #{
44             # my (@sub) = ( join ("", ) =~ /\n\s*sub\s+(\w+)/gs );
45             # foreach my $sub_i (sort @sub ) { print "=>head2 $sub_i\n" ;}
46             #}
47             #__DATA__
48            
49             ###############
50             # AUTOLOADERS #
51             ###############
52            
53             sub get_url {
54 0     0 0 0 require XML::Smart::Tree ;
55 0         0 *get_url = \*XML::Smart::Tree::get_url ;
56 0         0 &XML::Smart::Tree::get_url(@_) ;
57             }
58            
59             #######
60             # NEW #
61             #######
62            
63             sub new {
64 9     9 0 8186 my $class = shift ;
65 9         19 my $dtd = shift ;
66 9         28 $dtd =~ s/^file:\/\/\/?// ;
67            
68 9         41 my $this = bless({} , $class) ;
69            
70 9         48 $this->{tree} = $this->ParseDTD($dtd) ;
71 9         25 return $this ;
72             }
73            
74             ################################################################################
75            
76             ###############
77             # ELEM_EXISTS #
78             ###############
79            
80             sub elem_exists {
81 84     84 1 112 my $this = shift ;
82 84         102 my ( $tag ) = @_ ;
83 84 50       578 return 1 if $this->{tree}{$tag} ;
84 0         0 return undef ;
85             }
86            
87             ################
88             # CHILD_EXISTS #
89             ################
90            
91             sub child_exists {
92 69     69 1 80 my $this = shift ;
93 69         83 my ( $tag , $child ) = @_ ;
94 69 100 33     278 return undef if !$this->{tree}{$tag} || !$this->{tree}{$tag}{children} ;
95 66 100       231 return 1 if $this->{tree}{$tag}{children}{$child} ;
96 27         145 return undef ;
97             }
98            
99             ################
100             # IS_ELEM_UNIQ #
101             ################
102            
103             sub is_elem_uniq {
104 3     3 1 8 my $this = shift ;
105 3 50       14 return ( $this->get_elem_opt(@_) =~ /^[\!]?$/ ) ? 1 : undef ;
106             }
107            
108             ######################
109             # IS_ELEM_CHILD_UNIQ #
110             ######################
111            
112             sub is_elem_child_uniq {
113 0     0 1 0 my $this = shift ;
114 0 0       0 return ( $this->get_elem_child_opt(@_) =~ /^[\!]?$/ ) ? 1 : undef ;
115             }
116            
117             #################
118             # IS_ELEM_MULTI #
119             #################
120            
121             sub is_elem_multi {
122 3     3 1 5 my $this = shift ;
123 3 50       11 return ( $this->get_elem_opt(@_) =~ /^[\+\*]$/ ) ? 1 : undef ;
124             }
125            
126             #######################
127             # IS_ELEM_CHILD_MULTI #
128             #######################
129            
130             sub is_elem_child_multi {
131 9     9 1 12 my $this = shift ;
132 9 100       20 return ( $this->get_elem_child_opt(@_) =~ /^[\+\*]$/ ) ? 1 : undef ;
133             }
134            
135             ###############
136             # IS_ELEM_REQ #
137             ###############
138            
139             sub is_elem_req {
140 6     6 1 12 my $this = shift ;
141 6 100       22 return ( $this->get_elem_opt(@_) =~ /^[\!\+]?$/ ) ? 1 : undef ;
142             }
143            
144             #####################
145             # IS_ELEM_CHILD_REQ #
146             #####################
147            
148             sub is_elem_child_req {
149 54     54 1 93 my $this = shift ;
150 54 100       95 return ( $this->get_elem_child_opt(@_) =~ /^[\!\+]?$/ ) ? 1 : undef ;
151             }
152            
153             ###############
154             # IS_ELEM_OPT #
155             ###############
156            
157             sub is_elem_opt {
158 3     3 1 10 my $this = shift ;
159 3 50       13 return ( $this->get_elem_opt(@_) =~ /^[\?\*]$/ ) ? 1 : undef ;
160             }
161            
162             #####################
163             # IS_ELEM_CHILD_OPT #
164             #####################
165            
166             sub is_elem_child_opt {
167 0     0 1 0 my $this = shift ;
168 0 0       0 return ( $this->get_elem_child_opt(@_) =~ /^[\?\*]$/ ) ? 1 : undef ;
169             }
170            
171             ################
172             # GET_ELEM_OPT #
173             ################
174            
175             sub get_elem_opt {
176 15     15 1 21 my $this = shift ;
177 15         26 my ( $tag ) = @_ ;
178 15 50       52 return undef if !$this->{tree}{$tag} ;
179 15         159 return $this->{tree}{$tag}{option} ;
180             }
181            
182             ######################
183             # GET_ELEM_CHILD_OPT #
184             ######################
185            
186             sub get_elem_child_opt {
187 63     63 1 63 my $this = shift ;
188 63         72 my ( $tag , $child ) = @_ ;
189 63 50 33     274 return undef if !$this->{tree}{$tag} || !$this->{tree}{$tag}{children} ;
190 63         371 return $this->{tree}{$tag}{children}{$child} ;
191             }
192            
193             ###############
194             # IS_ELEM_ANY #
195             ###############
196            
197             sub is_elem_any {
198 0     0 1 0 my $this = shift ;
199 0         0 my ( $tag ) = @_ ;
200 0 0       0 return undef if !$this->{tree}{$tag} ;
201            
202 0 0       0 return 1 if $this->{tree}{$tag}{any} ;
203 0         0 return undef ;
204             }
205            
206             ##################
207             # IS_ELEM_PCDATA #
208             ##################
209            
210             sub is_elem_pcdata {
211 45     45 1 77 my $this = shift ;
212 45         48 my ( $tag ) = @_ ;
213 45 50       103 return undef if !$this->{tree}{$tag} ;
214 45 100       161 return 1 if $this->{tree}{$tag}{content} ;
215             }
216            
217             #################
218             # IS_ELEM_EMPTY #
219             #################
220            
221             sub is_elem_empty {
222 48     48 1 71 my $this = shift ;
223 48         55 my ( $tag ) = @_ ;
224 48 50       114 return undef if !$this->{tree}{$tag} ;
225            
226 48 100       126 return 1 if $this->{tree}{$tag}{empty} ;
227 42         150 return undef ;
228             }
229            
230             ##################
231             # IS_ELEM_PARENT #
232             ##################
233            
234             sub is_elem_parent {
235 0     0 1 0 my $this = shift ;
236 0         0 my ( $tag , @chk_parent ) = @_ ;
237 0 0       0 return undef if !$this->{tree}{$tag} ;
238            
239 0 0       0 my @parents = ref($this->{tree}{$tag}{parent}) eq 'ARRAY' ? @{$this->{tree}{$tag}{parent}} : () ;
  0         0  
240 0         0 my %parents = map { $_ => 1 } @parents ;
  0         0  
241            
242 0         0 foreach my $chk_parent_i ( @chk_parent ) {
243 0 0       0 next if $chk_parent_i eq '' ;
244 0 0       0 return undef if !$parents{$chk_parent_i} ;
245             }
246            
247 0         0 return 1 ;
248             }
249            
250             ###############
251             # ATTR_EXISTS #
252             ###############
253            
254             sub attr_exists {
255 42     42 1 55 my $this = shift ;
256 42         70 my ( $tag , @attrs ) = @_ ;
257 42 50       103 return undef if !$this->{tree}{$tag} ;
258            
259 42         61 foreach my $attrs_i ( @attrs ) {
260 48 100       170 return undef if !$this->{tree}{$tag}{attributes}{$attrs_i} ;
261             }
262            
263 39         146 return 1 ;
264             }
265            
266             ###############
267             # IS_ATTR_REQ #
268             ###############
269            
270             sub is_attr_req {
271 39     39 1 49 my $this = shift ;
272 39         58 my ( $tag , $attr ) = @_ ;
273            
274 39         141 _unset_sig_warn() ;
275 39         118 my $attr_check = $this->{tree}{$tag}{attributes}{$attr} ;
276 39         92 _reset_sig_warn() ;
277 39 100 33     214 return undef if( !$this->{tree}{$tag} || !$attr_check ) ;
278            
279 36         41 my $opt = @{$this->{tree}{$tag}{attributes}{$attr}}[1] ;
  36         88  
280            
281 36 100 66     268 return 1 if( $opt && ($opt =~ /#REQUIRED/i ) ) ;
282 6         27 return undef ;
283             }
284            
285             ###############
286             # IS_ATTR_FIX #
287             ###############
288            
289             sub is_attr_fix {
290 0     0 1 0 my $this = shift ;
291 0         0 my ( $tag , $attr ) = @_ ;
292 0 0 0     0 return undef if !$this->{tree}{$tag} || !$this->{tree}{$tag}{attributes}{$attr} ;
293            
294 0         0 my $opt = @{$this->{tree}{$tag}{attributes}{$attr}}[1] ;
  0         0  
295            
296 0 0       0 return 1 if $opt =~ /#FIXED/i ;
297 0         0 return undef ;
298             }
299            
300             #################
301             # GET_ATTR_TYPE #
302             #################
303            
304             sub get_attr_type {
305 0     0 1 0 my $this = shift ;
306 0         0 my ( $tag , $attr ) = @_ ;
307 0 0 0     0 return undef if !$this->{tree}{$tag} || !$this->{tree}{$tag}{attributes}{$attr} ;
308            
309 0         0 my $type = @{$this->{tree}{$tag}{attributes}{$attr}}[0] ;
  0         0  
310 0         0 return $type ;
311             }
312            
313            
314             ################
315             # GET_ATTR_DEF #
316             ################
317            
318             sub get_attr_def {
319 6     6 1 12 my $this = shift ;
320 6         12 my ( $tag , $attr ) = @_ ;
321 6 50 33     46 return () if !$this->{tree}{$tag} || !$this->{tree}{$tag}{attributes}{$attr} ;
322 6         10 my $def = @{$this->{tree}{$tag}{attributes}{$attr}}[2] ;
  6         18  
323 6         27 return $def ;
324             }
325            
326             ###################
327             # GET_ATTR_VALUES #
328             ###################
329            
330             sub get_attr_values {
331 0     0 1 0 my $this = shift ;
332 0         0 my ( $tag , $attr ) = @_ ;
333 0 0 0     0 return () if !$this->{tree}{$tag} || !$this->{tree}{$tag}{attributes}{$attr} ;
334 0         0 my $vals = @{$this->{tree}{$tag}{attributes}{$attr}}[3] ;
  0         0  
335            
336 0 0       0 return @$vals if ref $vals eq 'ARRAY' ;
337 0         0 return () ;
338             }
339            
340             ##############
341             # GET_CHILDS #
342             ##############
343            
344             sub get_childs {
345 30     30 1 35 my $this = shift ;
346 30         32 my ( $tag ) = @_ ;
347 30 50       73 return undef if !$this->{tree}{$tag} ;
348 30 50 33     68 return @{$this->{tree}{$tag}{childrenARR}} if $this->{tree}{$tag}{childrenARR} && @{$this->{tree}{$tag}{childrenARR}} ;
  30         140  
  30         102  
349 0         0 return () ;
350             }
351            
352             ##################
353             # GET_CHILDS_REQ #
354             ##################
355            
356             sub get_childs_req {
357 15     15 1 29 my $this = shift ;
358 15         32 my ( $tag ) = @_ ;
359            
360 15         36 my @childs = $this->get_childs($tag) ;
361            
362 15         23 my @childs_req ;
363 15         26 foreach my $child_i ( @childs ) {
364 54 100       106 push(@childs_req , $child_i) if $this->is_elem_child_req($tag , $child_i) ;
365             }
366            
367 15         50 return @childs_req ;
368             }
369            
370             #############
371             # GET_ATTRS #
372             #############
373            
374             sub get_attrs {
375 36     36 1 44 my $this = shift ;
376 36         49 my ( $tag ) = @_ ;
377 36 100 33     181 return undef if !$this->{tree}{$tag} || !$this->{tree}{$tag}{attr_order} ;
378            
379 30         29 my @attrs = @{$this->{tree}{$tag}{attr_order}} ;
  30         99  
380 30         105 return @attrs ;
381             }
382            
383             #################
384             # GET_ATTRS_REQ #
385             #################
386            
387             sub get_attrs_req {
388 18     18 1 1473 my $this = shift ;
389 18         25 my ( $tag ) = @_ ;
390            
391 18         42 my @attrs = $this->get_attrs($tag) ;
392            
393 18         40 my @attr_req ;
394 18         26 foreach my $attrs_i ( @attrs ) {
395 39 100       96 push(@attr_req , $attrs_i) if $this->is_attr_req($tag , $attrs_i) ;
396             }
397            
398 18         64 return @attr_req ;
399             }
400            
401             #########
402             # ERROR #
403             #########
404            
405             sub error {
406 6     6 1 11 my $this = shift ;
407            
408 6 50       21 if ( @_ ) { push( @{$this->{ERRORS}} , @_) ;}
  6         8  
  6         23  
409            
410 6 50 33     27 return @{ $this->{ERRORS} } if $this->{ERRORS} && @{$this->{ERRORS}} ;
  6         413  
  6         26  
411 0         0 return () ;
412             }
413            
414             ########
415             # TREE #
416             ########
417            
418 12     12 1 56 sub tree { return $_[0]->{tree} ; }
419            
420             ########
421             # ROOT #
422             ########
423            
424 0     0 1 0 sub root { return $_[0]->{root} ; }
425            
426             ############
427             # PARSEDTD #
428             ############
429            
430             sub ParseDTD {
431 9     9 0 13 my $this = shift ;
432 9         41 my $xml = read_data( shift(@_) ) ;
433 9         39 $this->{DATA} = $xml ;
434            
435 9         14 my (%elements, %definitions) ;
436            
437 9         243 $xml =~ s/\s+/ /gs ;
438            
439 9         241 while ($xml =~ s{}{}io) {
440 0         0 my ($percent, $entity, $include) = ($1,$2,$3) ;
441 0 0       0 $percent = '&' unless $percent;
442 0         0 my $definition = read_data($include) ;
443 0         0 $definition =~ s/\s+/ /gs ;
444 0         0 $xml =~ s{\Q$percent$entity;\E}{$definition}g ;
445             }
446            
447 9         34 $xml =~ s{}{}gs ;
448 9         34 $xml =~ s{<\?.*?\?>}{}gs ;
449            
450 9         137 while ($xml =~ s{}{}io) {
451 0         0 my ($percent, $entity, $definition) = ($1,$2,$3) ;
452 0 0       0 $percent = '&' unless $percent ;
453 0         0 $definitions{"$percent$entity"} = $definition ;
454             }
455            
456             {
457 9         19 my $replacements = 0 ;
  9         17  
458 9 0 33     160 1 while ++$replacements < 1000 and $xml =~ s{([&%]$name);}{(exists $definitions{$1} ? $definitions{$1} : "$1\x01;")}ge;
  0         0  
459 9 50       114 $this->error("Recursive or too many entities!") if $xml =~ m{([&%]$name);} ;
460             }
461 9         23 undef %definitions ;
462            
463 9         36 $xml =~ tr/\x01//d ;
464            
465 9         286 while ($xml =~ s{}{}io) {
466 45         134 my ($element, $children, $option) = ($1,$2,$3);
467            
468 45         132 $elements{$element}->{childrenSTR} = $children . $option ;
469 45         109 $children =~ s/\s//g ;
470            
471 45 100 33     213 if ($children eq '(#PCDATA)') { $children = '#PCDATA' ;}
  27 50       40  
472             elsif ( $children =~ s/^\((#PCDATA(?:\|$name)+)\)$/$1/o && $option eq '*') {
473 0         0 $children =~ s/\|/*,/g ;
474 0         0 $children .= '*' ;
475             }
476 18         61 else { $children = simplify_children( $children, $option) ;}
477            
478 45 50       555 $this->error(" is not valid!") unless $children =~ m{^#?$nameX(?:,$nameX)*$} ;
479            
480 45         102 $elements{$element}->{childrenARR} = [] ;
481            
482 45         119 foreach my $child (split ',', $children) {
483 75 100 66     349 $child =~ s/([\?\*\+])$//
484             and $option = $1
485             or $option = '!' ;
486            
487 75         227 $elements{$element}->{children}->{$child} = $option ;
488 75 100       165 push @{$elements{$element}->{childrenARR}}, $child unless $child eq '#PCDATA' ;
  48         120  
489             }
490            
491 45 100       77 delete $elements{$element}->{childrenARR} if !@{$elements{$element}->{childrenARR}} ;
  45         397  
492             }
493            
494 9         222 while ($xml =~ s{}{}io) {
495 6         23 my ($element, $param) = ($1,$2) ;
496 6 50       40 if ( uc($param) eq 'ANY') { $elements{$element}->{any} = 1 ;}
  0 50       0  
497 6         52 elsif ( uc($param) eq 'EMPTY') { $elements{$element}->{empty} = 1 ;}
498             }
499            
500 9         188 while ($xml =~ s{}{}io) {
501 15         60 my ($element, $attributes) = ($1,$2);
502            
503 15 50       41 $this->error(" referenced by an not found!") unless exists $elements{$element} ;
504            
505 15         498 while ($attributes =~ s/^\s*$AttDef//io) {
506 33         134 my ($name,$type,$option,$default) = ($1,$2,$3,$4);
507            
508 33 100 66     173 if ( $default && ( $default =~ /^"(.*?)"$/ ) ) { $default = $1 ; $default =~ s/\\"/"/gs ;}
  9 50 33     16  
  9         14  
509 0         0 elsif ( $default && ( $default =~ /^'(.*?)'$/ ) ) { $default = $1 ; $default =~ s/\\'/'/gs ;}
  0         0  
510            
511 33         140 $elements{$element}->{attributes}->{$name} = [$type,$option,$default,undef];
512            
513 33         38 push(@{$elements{$element}->{attr_order}} , $name) ;
  33         82  
514            
515 33 100       222 if ($type =~ /^(?:NOTATION\s*)?\(\s*(.*?)\)$/) {
516 9         27 $elements{$element}->{attributes}->{$name}->[3] = parse_values($1);
517             }
518             }
519             }
520            
521 9         59 $xml =~ s/\s+/ /gs ;
522            
523 9 50       204 if ( $xml =~ /^\s*<\!DOCTYPE\s+($name)\s*\[\s*(.*)$/ ) {
524 9         30 $this->{root} = $1 ;
525 9         18 my $data = $2 ;
526 9         34 $data =~ s/\s*]\s*>\s*$//gi ;
527 9         152 $xml = $data ;
528             }
529            
530 9 50       33 $this->error("UNPARSED DATA:\n$xml\n\n") if $xml =~ /\S/ ;
531            
532 9         45 foreach my $element (keys %elements) {
533 51         58 foreach my $child (keys %{$elements{$element}->{children}}) {
  51         169  
534 75 100       131 if ($child eq '#PCDATA') {
535 27         49 delete $elements{$element}->{children}->{'#PCDATA'};
536 27         70 $elements{$element}->{content} = 1;
537             }
538             else {
539 48 100       181 $this->error("Element $child referenced by $element was not found!") unless exists $elements{$child} ;
540            
541 48 100       106 if (exists $elements{$child}->{parent}) { push @{$elements{$child}->{parent}}, $element ;}
  3         6  
  3         9  
542 45         99 else { $elements{$child}->{parent} = [$element] ;}
543            
544 48         137 $elements{$child}->{option} = $elements{$element}->{children}->{$child} ;
545             }
546             }
547            
548 51 100       74 if ( !%{$elements{$element}->{children}} ) { delete $elements{$element}->{children} ;}
  51         173  
  33         78  
549             }
550            
551 9         42 return \%elements ;
552             }
553            
554             ##########
555             # CUTDTD #
556             ##########
557            
558             sub CutDTD {
559 4     4 0 10 my $this = shift ;
560 4 50       16 if ( !@_ ) { push(@_ , $this->{DATA} ) ;}
  4         27  
561            
562 4         16 my $xml = read_data( shift(@_) ) ;
563            
564 4         11 my (%elements, %definitions) ;
565            
566 4         13 $xml =~ s/\r\n?/\n/gs ;
567            
568 4         7 my $dtd_data ;
569            
570 4         211 while ($xml =~ s{()}{}io) {
571 0         0 $dtd_data .= "$1\n" ;
572             }
573            
574 4         17 $xml =~ s{}{}gs ;
575 4         16 $xml =~ s{<\?.*?\?>}{}gs ;
576            
577 4         119 while ($xml =~ s{()}{}io) {
578 0         0 $dtd_data .= "$1\n" ;
579             }
580            
581             {
582 4         8 my $replacements = 0 ;
  4         11  
583 4 0 33     129 1 while ++$replacements < 1000 and $xml =~ s{([&%]$name);}{(exists $definitions{$1} ? $definitions{$1} : "$1\x01;")}ge;
  0         0  
584 4 50       142 $this->error("Recursive or too many entities!") if $xml =~ m{([&%]$name);} ;
585             }
586 4         12 undef %definitions ;
587            
588 4         20 $xml =~ tr/\x01//d ;
589            
590 4         177 while ($xml =~ s{()}{}io) {
591 18         179 $dtd_data .= "$1\n" ;
592             }
593            
594 4         173 while ($xml =~ s{()}{}io) {
595 3         23 $dtd_data .= "$1\n" ;
596             }
597            
598 4         142 while ($xml =~ s{()}{}ios) {
599 7         63 $dtd_data .= "$1\n" ;
600             }
601            
602 4 50       199 if ( $xml =~ /^\s*<\!DOCTYPE\s+($name)\s*\[\s*/ ) {
603 4         17 $dtd_data = "\n" ;
604             }
605            
606 4         23 return $dtd_data ;
607             }
608            
609             ####################
610             # FLATTEN_CHILDREN #
611             ####################
612            
613             sub flatten_children {
614 18     18 0 37 my ( $children , $option ) = @_ ;
615            
616 18 100       57 if ($children =~ /\|/) {
617 3         89 $children =~ s/(\|$name)/${1}?/gs ;
618 3         14 $children =~ s{\|}{?,}g ;
619             }
620            
621 18 50       45 if ($option) {
622 0         0 $children =~ s/,/$option,/g ;
623 0         0 $children .= $option ;
624             }
625            
626 18         97 return $children ;
627             }
628            
629             #####################
630             # SIMPLIFY_CHILDREN #
631             #####################
632            
633             sub simplify_children {
634 18     18 0 36 my ( $children, $option ) = @_;
635            
636 18         244 1 while $children =~ s{\(($nameX(?:[,|]$nameX)*)\)([\?\*\+]*)}{flatten_children($1, $2)}geo ;
  18         49  
637            
638 18 50       40 if ($option) {
639 0         0 $children =~ s/,/$option,/g ;
640 0         0 $children .= $option ;
641             }
642            
643 18         37 foreach ($children) {
644 18         28 s{\?\?}{?}g;
645 18         38 s{\?\+}{*}g;
646 18         28 s{\?\*}{*}g;
647 18         27 s{\+\?}{*}g;
648 18         30 s{\+\+}{+}g;
649 18         27 s{\+\*}{*}g;
650 18         24 s{\*\?}{*}g;
651 18         24 s{\*\+}{*}g;
652 18         39 s{\*\*}{*}g;
653             }
654            
655 18         46 return $children ;
656             }
657            
658             ################
659             # PARSE_VALUES #
660             ################
661            
662             sub parse_values {
663 9     9 0 17 my $def = shift ;
664            
665 9         16 $def =~ s/^\s*\(\s*// ;
666 9         17 $def =~ s/\s*\)\s*$// ;
667 9         17 $def = "|$def" ;
668            
669 9         16 my @def ;
670 9         267 while( $def =~ /\s*|\s*(?:($RE_quoted)|([^\(\)\|]+))/gs ) {
671 105 100       597 if ( defined $1 ) {
    100          
672 3         8 my $q = $1 ;
673 3 50       19 if ( $q =~ /^"(.*?)"$/ ) { $q = $1 ; $q =~ s/\\"/"/gs ;}
  3 0       8  
  3         9  
674 0         0 elsif ( $q =~ /^'(.*?)'$/ ) { $q = $1 ; $q =~ s/\\'/'/gs ;}
  0         0  
675 3         19 push(@def , $q) ;
676             }
677             elsif ( defined $2 ) {
678 30         43 my $d = $2 ;
679 30         46 $d =~ tr/\x20\x09\x0D\x0A//d ; # get rid of whitespace
680 30         152 push(@def , $d) ;
681             }
682             }
683            
684 9         395 foreach my $def_i ( @def ) {
685            
686             }
687            
688 9         95 return \@def ;
689             }
690            
691             #############
692             # READ_DATA #
693             #############
694            
695             sub read_data {
696 13     13 0 28 my $data ;
697             {
698 13         27 my ($fh,$open) ;
  13         25  
699            
700 13 50       157 if (ref($_[0]) eq 'GLOB') { $fh = $_[0] ;}
  0 50       0  
    50          
701 0         0 elsif ($_[0] =~ /^http:\/\/\w+[^\r\n]+$/s) { $data = &get_url($_[0]) ;}
702 13         31 elsif ($_[0] =~ /<.*?>/s) { $data = $_[0] ;}
703 0         0 else { open ($fh,$_[0]) ; binmode($fh) ; $open = 1 ;}
  0         0  
  0         0  
704            
705 13 50       47 if ($fh) {
706 0         0 1 while( read($fh, $data , 1024*8 , length($data) ) ) ;
707 0 0       0 close($fh) if $open ;
708             }
709             }
710            
711 13         35 return $data ;
712             }
713            
714             ################################################################################
715            
716             #############
717             # APPLY_DTD #
718             #############
719            
720             sub apply_dtd {
721 6     6 0 110 my $xml = shift ;
722 6         11 my $dtd = shift ;
723            
724 6 50       29 if ( ref($dtd) ne 'XML::Smart::DTD' ) { $dtd = XML::Smart::DTD->new($dtd , @_) ;}
  6         94  
725            
726 6         272 $$xml->{DTD} = $dtd ;
727            
728 6 50 33     42 return if !$dtd || !$dtd->tree || !%{ $dtd->tree } ;
  6   33     16  
729            
730 6         32 _apply_dtd($dtd , $xml->tree , undef , undef , {} , undef , undef , {} , @_) ;
731             }
732            
733             sub _apply_dtd {
734            
735 66     66   167 my ($dtd , $tree , $tag , $ar_i , $prev_tree , $prev_tag , $prev_exists , $parsed , %opts) = @_ ;
736 66         156 _unset_sig_warn() ;
737            
738             ##print "$tag>> $tree , $tag , $prev_tree , $prev_tag , $parsed >> $opts{no_delete}\n" ;
739            
740 66 100       148 if ( ref($tree) ) {
741 57 50       132 if ($$parsed{"$tree"}) {
742 0         0 _reset_sig_warn() ;
743 0         0 return ;
744             }
745 57         141 ++$$parsed{"$tree"} ;
746             }
747            
748 66 100       198 if (ref($tree) eq 'HASH') {
    100          
749            
750 51 100 66     183 if ( $tag ne '' && $dtd->elem_exists($tag) ) {
751 45 100       87 if ( $dtd->is_elem_empty($tag) ) {
    100          
752 3         15 $prev_tree->{$tag} = {} ;
753             }
754             elsif ( $dtd->is_elem_pcdata($tag) ) {
755 27 100       61 if ( ref $prev_tree->{$tag} eq 'HASH' ) { $prev_tree->{$tag}{CONTENT} = '' if !defined $prev_tree->{$tag}{CONTENT} ;}
  27 50       82  
756 0 0       0 else { $prev_tree->{$tag} = '' if !defined $prev_tree->{$tag} ;}
757             }
758             else {
759 15         44 my @childs_req = $dtd->get_childs_req($tag) ;
760 15         31 foreach my $childs_req_i ( @childs_req ) {
761 33 100       76 if ( !exists $tree->{$childs_req_i} ) {
762 9         24 $tree->{$childs_req_i} = {} ;
763             }
764             }
765            
766 15         58 my @attrs_req = $dtd->get_attrs_req($tag) ;
767 15         29 foreach my $attrs_req_i ( @attrs_req ) {
768 24 100       56 if ( !exists $tree->{$attrs_req_i} ) {
769 3         14 $tree->{$attrs_req_i} = $dtd->get_attr_def($tag , $attrs_req_i) ;
770             }
771             }
772            
773             {
774 15         18 my @order = ($dtd->get_attrs($tag) , $dtd->get_childs($tag)) ;
  15         36  
775            
776 15 100       40 if ( ! $tree->{'/order'} ) { $tree->{'/order'} = \@order ;}
  10         41  
777             else {
778 5         8 my %in_order ;
779             {
780 5         7 my %n ; %in_order = map { $_ => (++$n{$_}) } @{ $tree->{'/order'} } ;
  5         7  
  5         7  
  20         56  
  5         10  
781             }
782            
783 5         12 my (@new_order , %order) ;
784 5         10 foreach my $order_i ( @order ) {
785 18         37 _unset_sig_warn() ;
786 18   100     65 push(@new_order , (($order_i) x ($in_order{$order_i} || 1))) ;
787 18         45 $order{$order_i} = 1 ;
788 18         38 _reset_sig_warn() ;
789             }
790            
791 5         9 foreach my $order_i ( @{ $tree->{'/order'} } ) {
  5         27  
792 20 50       45 next if $order{$order_i} ;
793 0         0 push(@new_order , $order_i) ;
794             }
795            
796 5         27 $tree->{'/order'} = \@new_order ;
797             }
798            
799             }
800            
801            
802             }
803            
804             }
805            
806 51         173 foreach my $Key ( keys %$tree ) {
807 141 100 66     1043 if ($Key eq '' || $Key eq '/order' || $Key eq '/nodes' || $Key eq 'CONTENT') { next ;}
  66   100     117  
      100        
808            
809 75 100 66     351 if ( ( $tag eq '' && $dtd->elem_exists($Key)) || ( $tag ne '' && $dtd->child_exists($tag , $Key)) ) {
    50 66        
      66        
      33        
810 45 50 66     150 if ( $tree->{'/nodes'}{$Key} && ( $tree->{'/nodes'}{$Key} =~ /^(\w+,\d+),(\d*)/ ) ) { $tree->{'/nodes'}{$Key} = "$1,1" ;}
  0         0  
811 45         95 else { $tree->{'/nodes'}{$Key} = 1 ;}
812            
813 45 100       153 if ( !ref($tree->{$Key}) ) {
    100          
814 18 100       41 my $content = ( $tree->{$Key} ) ? $tree->{$Key} : '';
815 18 50       45 $tree->{$Key} = {} if !ref $tree->{$Key} ;
816 18 100       58 $tree->{$Key}{CONTENT} = $content if $content ne '' ;
817             }
818             elsif ( ref($tree->{$Key}) eq 'ARRAY' ) {
819 9 100 66     55 if ( $tag ne '' && !$dtd->is_elem_child_multi($tag , $Key) ) {
820 3         9 $tree->{$Key} = $tree->{$Key}[0] ;
821             }
822             }
823            
824 45         207 _apply_dtd($dtd , $tree->{$Key} , $Key , undef , $tree , $tag , 1, $parsed , %opts) ;
825             } elsif ( $tag ne '' && $dtd->attr_exists($tag , $Key) ) {
826 30         45 delete $tree->{'/nodes'}{$Key} ;
827 30 50 66     109 if ( ref($tree->{$Key}) eq 'HASH' && exists $tree->{$Key}{CONTENT} && (keys %{$tree->{$Key}}) == 1 ) {
  3   66     17  
828 0         0 my $content = $tree->{$Key}{CONTENT} ;
829 0         0 $tree->{$Key} = $content ;
830             }
831            
832 30 100       57 if ( ref $tree->{$Key} ) {
833 3 50       19 if ( ref $tree->{$Key} eq 'ARRAY' ) { $tree->{$Key} = $tree->{$Key}[0] ;}
  0         0  
834 3 50       13 if ( ref $tree->{$Key} eq 'HASH' ) { $tree->{$Key} = $tree->{$Key}{CONTENT} ;}
  3         10  
835             }
836            
837 30 100 66     159 if ( $tag ne '' && $tree->{$Key} eq '' ) {
838 3         11 $tree->{$Key} = $dtd->get_attr_def($tag , $Key) ;
839             }
840             }
841             else {
842 0 0 0     0 if ( $prev_exists && !$opts{no_delete} ) { delete $tree->{$Key} ;}
  0         0  
843             else {
844 0         0 _apply_dtd($dtd , $tree->{$Key} , $Key , undef , $tree , $tag , undef , $parsed , %opts) ;
845             }
846            
847             }
848             }
849             }
850             elsif (ref($tree) eq 'ARRAY') {
851 6         8 my $i = -1 ;
852 6         11 foreach my $tree_i ( @$tree ) {
853 15         16 ++$i ;
854 15         104 _apply_dtd($dtd , $tree_i , $tag , $i , $prev_tree , $prev_tag , $prev_exists , $parsed , %opts) ;
855             }
856             }
857             else {
858 9 50 33     33 if ( $tag ne '' && $dtd->elem_exists($tag) ) {
    0 0        
859 9 50       24 if ( $prev_tree->{'/nodes'}{$tag} =~ /^(\w+,\d+),(\d*)/ ) { $prev_tree->{'/nodes'}{$tag} = "$1,1" ;}
  0         0  
860 9         16 else { $prev_tree->{'/nodes'}{$tag} = 1 ;}
861            
862 9 100 33     50 if ( !ref($prev_tree->{$tag}) || ( ref($prev_tree->{$tag}) eq 'HASH' && !exists $prev_tree->{$tag}{CONTENT}) ) {
      66        
863 3         7 my $content = $prev_tree->{$tag} ;
864 3 50       12 $prev_tree->{$tag} = {} if !ref $prev_tree->{$tag} ;
865 3 50       14 $prev_tree->{$tag}{CONTENT} = $content if $content ne '' ;
866             }
867             }
868             elsif ( $tag ne '' && $dtd->attr_exists($prev_tag , $tag) ) {
869 0         0 delete $prev_tree->{'/nodes'}{$tag} ;
870 0 0 0     0 if ( ref($prev_tree->{$tag}) eq 'HASH' && exists $prev_tree->{$tag}{CONTENT} && (keys %{$prev_tree->{$tag}}) == 1 ) {
  0   0     0  
871 0         0 my $content = $prev_tree->{$tag}{CONTENT} ;
872 0         0 $prev_tree->{$tag} = $content ;
873             }
874             }
875             }
876            
877 66 100       204 delete $$parsed{"$tree"} if ref($tree) ;
878            
879 66         142 _reset_sig_warn() ;
880 66         186 return 1 ;
881             }
882            
883             #######
884             # END #
885             #######
886            
887             1;
888            
889             __END__