File Coverage

blib/lib/simpleXMLParse.pm
Criterion Covered Total %
statement 12 242 4.9
branch 0 54 0.0
condition n/a
subroutine 4 12 33.3
pod 0 2 0.0
total 16 310 5.1


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