File Coverage

blib/lib/simpleXMLParse.pm
Criterion Covered Total %
statement 15 246 6.1
branch 0 54 0.0
condition n/a
subroutine 5 13 38.4
pod 0 2 0.0
total 20 315 6.3


line stmt bran cond sub pod time code
1             package simpleXMLParse;
2              
3             # Perl Module: simpleXMLParse
4             # Author: Daniel Edward Graham
5             # Copyright (c) Daniel Edward Graham 2008-2015
6             # Date: 4/06/2015
7             # License: LGPL 3.0
8             #
9              
10             require Exporter;
11 1     1   3621 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         82  
12             @ISA = qw(Exporter);
13              
14             # This allows declaration use simpleXMLParse ':all';
15             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
16             # will save memory.
17             %EXPORT_TAGS = ( 'all' => [ qw(
18            
19             ) ] );
20              
21             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
22              
23             @EXPORT = qw(
24            
25             );
26              
27             $VERSION = '3.0';
28              
29 1     1   3 use Carp;
  1         1  
  1         56  
30 1     1   3 use strict;
  1         4  
  1         26  
31 1     1   2 no warnings;
  1         1  
  1         27  
32              
33 1     1   370 use open ':encoding(utf8)';
  1         1682  
  1         3  
34              
35             my @cdata;
36             my $cdataInd = 0;
37             my $MAXIND = 10000;
38              
39             sub new {
40 0     0 0   my $class = shift;
41 0 0         my %args = (@_ == 1) ? ((ref($_[0]) eq 'HASH') ? %{$_[0]}:(input => $_[0])):@_;
  0 0          
42 0           my $altstyle = 0;
43 0           my $fn;
44 0           $fn = $args{"input"};
45 0 0         $altstyle = 1 if ($args{"style"} eq '2');
46 0           my $self = {};
47 0           $self->{"xml"} = undef;
48 0           $self->{"data"} = undef;
49 0 0         open( INFILE, "$fn" ) or croak "Unable to process [$fn]\n";
50 0           $self->{"xml"} = join '', ;
51 0           close(INFILE);
52 0           $self->{"data"} = _ParseXML( $self->{"xml"} );
53 0           my $ret = bless $self;
54 0 0         if ($altstyle) {
55 0           $ret->_convertToStyle();
56             }
57 0           $cdataInd = $cdataInd % $MAXIND;
58 0           return $ret;
59             }
60              
61             sub parse {
62 0     0 0   my $self = shift;
63 0           return $self->{data};
64             }
65              
66             sub _convertToStyle {
67 0     0     my $self = shift;
68 0           my @recursearr = ($self->{"data"});
69 0           while (@recursearr) {
70 0           my $i = pop @recursearr;
71 0 0         if (ref($i) eq "HASH") {
72 0           foreach my $j (keys %$i) {
73 0 0         if ($j =~ /^(.*?)\_(.*?)\_([0-9]+)\_attr$/) {
74 0           my ($attrnm, $tagnm, $cnt) = ($1, $2, $3);
75 0           my $n = undef;
76 0 0         if (ref($i->{$tagnm}) eq "ARRAY") {
77 0           my $hold;
78 0 0         if (ref($i->{$tagnm}->[$cnt]) eq '') {
79 0           $hold = $i->{$tagnm}->[$cnt];
80 0           $i->{$tagnm}->[$cnt] = { };
81 0 0         if ($hold !~ /^\s*$/ ) {
82 0           $i->{$tagnm}->[$cnt]->{VALUE} = $hold;
83             }
84             }
85 0           while (defined($i->{$tagnm}->[$cnt]->{$attrnm.$n})) {
86 0           $n++;
87             }
88 0           $i->{$tagnm}->[$cnt]->{$attrnm.$n} = $i->{$j};
89             } else {
90 0 0         if (ref($i->{$tagnm}) eq "HASH") {
91 0           my $n = undef;
92 0           while (defined($i->{$tagnm}->{$attrnm.$n})) {
93 0           $n++;
94             }
95 0           $i->{$tagnm}->{$attrnm.$n} = $i->{$j};
96             } else {
97 0           my $hold;
98 0           $hold = $i->{$tagnm};
99 0           $i->{$tagnm} = { };
100 0 0         if ($hold !~ /^\s*$/) {
101 0           $i->{$tagnm}->{VALUE} = $hold;
102             }
103 0           $i->{$tagnm}->{$attrnm} = $i->{$j};
104             }
105             }
106 0           delete $i->{$j};
107             } else {
108 0           push @recursearr, $i->{$j};
109             }
110             }
111             } else {
112 0 0         if (ref($i) eq "ARRAY") {
113 0           foreach my $j (@$i) {
114 0           push @recursearr, $j;
115             }
116             }
117             }
118             }
119             }
120              
121             sub _cdatasub {
122 0     0     my $cdata = shift;
123 0           my $tmpind = $cdataInd++;
124 0           $cdata[$tmpind] = $cdata;
125 0           return "0x0CDATA0x0".($tmpind)."0x0";
126             }
127            
128             sub _cdatasubout {
129 0     0     my $ind = shift;
130 0           my $cdata = $cdata[$ind];
131 0           return $cdata;
132             }
133              
134             sub _unescp {
135 0     0     my $firsttag = shift;
136 0           $firsttag =~ s/\\\\/\\/gs;
137 0           $firsttag =~ s/\\\*/\*/gs;
138 0           $firsttag =~ s/\\\|/\|/gs;
139 0           $firsttag =~ s/\\\$/\$/gs;
140 0           $firsttag =~ s/\\\?/\?/gs;
141 0           $firsttag =~ s/\\\{/\{/gs;
142 0           $firsttag =~ s/\\\}/\}/gs;
143 0           $firsttag =~ s/\\\(/\(/gs;
144 0           $firsttag =~ s/\\\)/\)/gs;
145 0           $firsttag =~ s/\\\+/\+/gs;
146 0           $firsttag =~ s/\\\[/\[/gs;
147 0           $firsttag =~ s/\\\]/\]/gs;
148 0           $firsttag =~ s/\\\./\./gs;
149 0           $firsttag =~ s/\\\^/\^/gs;
150 0           $firsttag =~ s/\\\-/\-/gs;
151 0           return $firsttag;
152             }
153              
154             sub _entity {
155 0     0     my $text = shift;
156 0           $text =~ s/\<\;/\
157 0           $text =~ s/\>\;/\>/g;
158 0           $text =~ s/\&\;/\&/g;
159 0           $text =~ s/\&apos\;/\'/g;
160 0           $text =~ s/\"\;/\"/g;
161 0           return $text;
162             }
163              
164             sub _ParseXML {
165 0     0     my ($xml) = @_;
166             # $xml =~ s/\n//g;
167 0           $xml =~ s/\<\!\[CDATA\[(.*?)\]\]\>/&_cdatasub($1)/egs;
  0            
168 0           $xml =~ s/\<\!\-\-.*?\-\-\>//gs;
169 0           $xml =~ s/\<\?xml.*?\?\>//gs;
170 0           $xml =~ s/\<\?[^\>]*?\?\>//gs;
171 0           $xml =~ s/\<\!\-\-[^\>]*?\-\-\>//gs;
172 0           $xml =~ s/\<\!ELEMENT[^\>]*?\>//gs;
173 0           $xml =~ s/\<\!ENTITY[^\>]*?\>//gs;
174 0           $xml =~ s/\<\!ATTLIST[^\>]*?\>//gs;
175 0           $xml =~ s/\<\!DOCTYPE[^\>]*?\>//gs;
176 0           my $rethash = ();
177 0           my @retarr;
178 0           my $firsttag = $xml;
179 0           my ( $attr, $innerxml, $xmlfragment );
180 0           $firsttag =~ s/^[\s\n]*\<([^\s\>\n\/]*).*$/$1/gs;
181 0           $firsttag =~ s/\\/\\\\/gs;
182 0           $firsttag =~ s/\*/\\\*/gs;
183 0           $firsttag =~ s/\|/\\\|/gs;
184 0           $firsttag =~ s/\$/\\\$/gs;
185 0           $firsttag =~ s/\?/\\\?/gs;
186 0           $firsttag =~ s/\{/\\\{/gs;
187 0           $firsttag =~ s/\}/\\\}/gs;
188 0           $firsttag =~ s/\(/\\\(/gs;
189 0           $firsttag =~ s/\)/\\\)/gs;
190 0           $firsttag =~ s/\+/\\\+/gs;
191 0           $firsttag =~ s/\[/\\\[/gs;
192 0           $firsttag =~ s/\]/\\\]/gs;
193 0           $firsttag =~ s/\./\\\./gs;
194 0           $firsttag =~ s/\^/\\\^/gs;
195 0           $firsttag =~ s/\-/\\\-/gs;
196              
197 0 0         if ( $xml =~ /^[\s\n]*\<${firsttag}(\>|[\s\n]\>|[\s\n][^\>]*[^\/]\>)(.*?)\<\/${firsttag}[\s\n]*\>(.*)$/s )
198             {
199 0           $attr = $1;
200 0           $innerxml = $2;
201 0           $xmlfragment = $3;
202 0           $attr =~ s/\>$//gs;
203             }
204             else {
205 0 0         if ( $xml =~ /^[\s\n]*\<${firsttag}(\/\>|[\s\n][^\>]*\/\>)(.*)$/s ) {
206 0           $attr = $1;
207 0           $innerxml = "";
208 0           $xmlfragment = $2;
209             } else {
210 0 0         if (!ref($xml)) {
211 0           $xml = _entity($xml);
212 0           $xml =~ s/0x0CDATA0x0(\d+?)0x0/&_cdatasubout($1)/egs;
  0            
213             }
214 0           return $xml;
215             }
216             }
217 0           my $ixml = $innerxml;
218 0           while ($ixml =~ /^.*?\<${firsttag}(\>|[\s\n]\>|[\s\n][^\>]*[^\/]\>)(.*?)$/s) {
219 0           $ixml = $2;
220 0           $innerxml .= "";
221 0 0         if ($xmlfragment =~ /^(.*?)\<\/${firsttag}[\s\n]*\>(.*)$/s) {
222 0           my $ix = $1;
223 0           $innerxml .= $ix;
224 0           $ixml .= $ix;
225 0           $xmlfragment = $2;
226             } else {
227 0           die "Invalid XML innerxml: $innerxml\nixml: $ixml\nxmlfragment: $xmlfragment\n";
228             }
229             }
230 0           my $nextparse = _ParseXML($innerxml);
231 0           $rethash->{&_unescp($firsttag)} = $nextparse;
232 0           my @attrarr;
233 0           while ( $attr =~ s/^[\s\n]*([^\s\=\n]+)\s*\=\s*(\".*?\"|\'.*?\')(.*)$/$3/gs ) {
234 0           my ($name, $val) = ($1, $2);
235 0           $val =~ s/^\'(.*)\'$/$1/gs;
236 0           $val =~ s/^\"(.*)\"$/$1/gs;
237 0           push @attrarr, $name;
238 0           push @attrarr, _entity($val);
239             }
240 0           my $attrcnt = 0;
241 0           while ( my $val = shift(@attrarr) ) {
242 0           $rethash->{ "$val" . "_".&_unescp(${firsttag})."_" . $attrcnt . "_attr" } = shift(@attrarr);
243             }
244 0           my $retflag = 0;
245 0           my ( $xmlfragment1, $xmlfragment2 );
246 0           my %attrhash;
247 0           $attrcnt++;
248 0           while (1) {
249 0 0         if ( $xmlfragment =~
250             /^(.*?)\<${firsttag}(\>|[\s\n]\>|[\s\n][^\>]*[^\/]\>)(.*?)\<\/${firsttag}[\s\n]*\>(.*)$/s )
251             {
252 0 0         if ( !$retflag ) {
253 0           push @retarr, $nextparse;
254             }
255 0           $retflag = 1;
256 0           $xmlfragment1 = $1;
257 0           $attr = $2;
258 0           $innerxml = $3;
259 0           $xmlfragment2 = $4;
260             } else {
261 0 0         if ( $xmlfragment =~ /^(.*?)\<${firsttag}(\/\>|[\s\n][^\>]*\/\>)(.*)$/s ) {
262 0 0         if ( !$retflag ) {
263 0           push @retarr, $nextparse;
264             }
265 0           $retflag = 1;
266 0           $xmlfragment1 = $1;
267 0           $attr = $2;
268 0           $innerxml = "";
269 0           $xmlfragment2 = $3;
270             } else {
271 0           last;
272             }
273             }
274 0           $attr =~ s/\>$//gs;
275 0           my %opening = ( );
276 0           my %closing = ( );
277 0           my $frag = $xmlfragment1;
278 0           while ($frag =~ /^(.*?)\<([^\s\n\/]+)[^\/]*?\>(.*)$/s) {
279 0           my $tg = $2;
280 0           $frag = $3;
281 0           $opening{$tg}++;
282             }
283 0           my $frag = $xmlfragment1;
284 0           while ($frag =~ /^(.*?)\<\/([^\s\n]+)\>(.*)$/s) {
285 0           my $tg = $2;
286 0           $frag = $3;
287 0           $closing{$tg}++;
288             }
289 0           my $flag = 0;
290 0           foreach my $k (keys %opening) {
291 0 0         if ($opening{$k} > $closing{$k}) {
292 0           $xmlfragment = $xmlfragment1 . "<${firsttag}0x0 ${attr}>${innerxml}". $xmlfragment2;
293 0           $flag = 1;
294 0           last;
295             }
296             }
297 0 0         next if ($flag);
298 0           my $ixml = $innerxml;
299 0           while ($ixml =~ /.*?\<${firsttag}(\>|[\s\n]\>|[\s\n][^\>]*[^\/]\>)(.*?)$/s) {
300 0           $ixml = $2;
301 0           $innerxml .= "";
302 0 0         if ($xmlfragment2 =~ /(.*?)\<\/${firsttag}[\s\n]*\>(.*)$/s) {
303 0           my $ix = $1;
304 0           $innerxml .= $ix;
305 0           $ixml .= $ix;
306 0           $xmlfragment2 = $2;
307             } else {
308 0           die "Invalid XML";
309             }
310             }
311 0           $xmlfragment = $xmlfragment1 . $xmlfragment2;
312 0           while ( $attr =~ s/^[\s\n]*([^\s\=\n]+)\s*\=\s*(\".*?\"|\'.*?\')(.*)$/$3/gs ) {
313 0           my ($name, $val) = ($1, $2);
314 0           $val =~ s/^\'(.*)\'$/$1/gs;
315 0           $val =~ s/^\"(.*)\"$/$1/gs;
316 0           push @attrarr, $name;
317 0           push @attrarr, _entity($val);
318             }
319 0           while ( my $val = shift(@attrarr) ) {
320 0           $rethash->{ "$val" . "_".&_unescp(${firsttag})."_" . $attrcnt . "_attr" } = shift(@attrarr);
321             }
322 0           $attrcnt++;
323 0           $nextparse = _ParseXML($innerxml);
324 0           push @retarr, $nextparse;
325             }
326 0 0         if (@retarr) {
327 0           $rethash->{_unescp($firsttag)} = \@retarr;
328             }
329 0           $xmlfragment =~ s/${firsttag}0x0/${firsttag}/gs;
330 0           my $remainderparse = _ParseXML($xmlfragment);
331 0           my $attrcnt;
332             my $attrfrag;
333 0 0         if ( ref($remainderparse) eq "HASH" ) {
334 0           foreach ( keys %{$remainderparse} ) {
  0            
335 0           $rethash->{&_unescp($_)} = $remainderparse->{&_unescp($_)};
336             }
337             }
338 0 0         if ( keys %{$rethash} ) {
  0            
339 0           return $rethash;
340             }
341             else {
342 0           return undef;
343             }
344             }
345              
346             1;
347             __END__