File Coverage

blib/lib/Data/Sofu/SofuML.pm
Criterion Covered Total %
statement 208 393 52.9
branch 47 150 31.3
condition 14 58 24.1
subroutine 20 31 64.5
pod 14 27 51.8
total 303 659 45.9


line stmt bran cond sub pod time code
1             ###############################################################################
2             #SofuML.pm
3             #Last Change: 2008-02-15
4             #Copyright (c) 2008 Marc-Seabstian "Maluku" Lucksch
5             #Version 0.29
6             ####################
7             #This file is part of the sofu.pm project, a parser library for an all-purpose
8             #ASCII file format. More information can be found on the project web site
9             #at http://sofu.sourceforge.net/ .
10             #
11             #sofu.pm is published under the terms of the MIT license, which basically means
12             #"Do with it whatever you want". For more information, see the license.txt
13             #file that should be enclosed with libsofu distributions. A copy of the license
14             #is (at the time of this writing) also available at
15             #http://www.opensource.org/licenses/mit-license.php .
16             ###############################################################################
17            
18             =head1 NAME
19            
20             Data::Sofu::SofuML - Interface to various binary drivers
21            
22             =head1 DESCRIPTION
23            
24             This Module is used to convert Sofu Trees to XML Tree.
25            
26             This is mostly for convertig Sofu data via XSLT or similar.
27            
28             You don't need to use this module directly.
29            
30             =head1 Synopsis
31            
32             use Data::Sofu;
33             my $sofu = readSofu("file.sofu");
34             writeSofuML("file.xml",$sofu,getSofucomments());
35             #And back
36             my $xml = readSofu("file.xml"); #readSofu can detect SofuML files.
37             writeSofu("file.sofu",$xml,getSofucomments());
38            
39             Using the Sofu OO syntax:
40            
41             require Data::Sofu;
42             my $s = Data::Sofu::new();
43             my $sofu = $s->read("file.sofu");
44             $s->writeML("file.xml",$sofu,$s->comments());
45             #And back
46             my $xml = $s->read("file.xml"); #read also detects SofuML files.
47             $s->write("file.sofu",$xml,$s->comments());
48            
49             Using scalars instead of files:
50            
51             require Data::Sofu;
52             my $s = Data::Sofu::new();
53             my $sofu = $s->read("file.sofu");
54             my $xmlstring = $s->packML($sofu,$s->comments()); #Contains now the xml files content
55             my $xml = $s->unpack($xmlstring);
56             $s->write($xml,$s->comments());
57            
58             But: $scalar=packML() is not the same as writeML(\$scalar), packML will not indent the file.
59            
60            
61            
62             =head1 SYNTAX
63            
64             This Module is pure OO, exports nothing
65            
66             =cut
67            
68             package Data::Sofu::SofuML;
69 1     1   6 use strict;
  1         3  
  1         39  
70 1     1   5 use warnings;
  1         2  
  1         63  
71            
72             our $VERSION="0.29";
73             #We are really going to need these modules:
74 1     1   5 use Encode;
  1         2  
  1         135  
75 1     1   6 use Carp qw/confess cluck/;
  1         2  
  1         5762  
76             require Data::Sofu;
77            
78             =head1 METHODS
79            
80             Most of these Methods (except pack()) are ony for internal use.
81            
82             =head2 new()
83            
84             Creates a new C and returns it.
85            
86             =cut
87            
88             sub new {
89 1     1 1 3 my $self={};
90 1         3 bless $self,shift;
91 1         10 $self->{IDS} = {};
92 1         3 $self->{ID} = 0;
93 1         2 $self->{INDENT} = "\t";
94 1         4 return $self;
95             }
96            
97             =head2 C
98            
99             Returns the (quite badly) escaped form of STRING
100            
101             =cut
102            
103             sub whiteescape {
104 52     52 0 56 my $self=shift;
105 52         77 my $data = shift;
106 52 100       186 return $data if ($data eq " ");
107 16         19 my $f = "";
108 16         20 $data=~s/([^\ \n])/sprintf("&#x%X;",ord($1))/eg;
  0         0  
109            
110 16         58 return $f.$data;
111             }
112            
113             sub XMLescape {
114 74     74 1 156 my $self=shift;
115 74         83 my $string=shift;
116 74         78 my $level=shift;
117 74         92 $string =~ s/\&/&/g;
118 74         83 $string =~ s/\
119 74         72 $string =~ s/\>/>/g;
120 74         94 $string =~ s/\"/"/g;
121 74         73 $string =~ s/\'/'/g;
122 74         151 $string=~s"^([\s\n\x0A]+)"join '',map {sprintf('&#x%X;' ,ord($_))} split //,$1"emg;
  20         46  
  24         114  
123 74         178 $string=~s/([\s\n\x0A]+)$/join '',map {sprintf('&#x%X;' ,ord($_))} split m##,$1/emg;
  16         34  
  20         83  
124 74         132 $string =~ s/([\s]+)/$self->whiteescape($1)/eg;
  52         105  
125 74         108 $string=~s/([\ ]{2,})/join '',map {sprintf('&#x%X;' ,ord($_))} split m##,$1/eg;
  4         11  
  16         40  
126 74         92 $string=~s/\n/"\n".$self->indent($level)/eg;
  12         22  
127             #$string=~s/\n/$self->indent($level)."\n"/eg;
128 74         242 return $string;
129             #return $self->indent($level).$string; #makes bad Juju with XSLT
130             }
131            
132             sub XMLunescape {
133 0     0 0 0 my $string=shift;
134 0         0 $string =~ s/^\s+//g;
135 0         0 $string =~ s/\s+$//g;
136 0         0 $string =~ s/\s*\n\s*/\n/g;
137 0         0 $string =~ s/[\s[^\n]]+/ /g;
138 0         0 $string =~ s/&#x([\dabcdefABCDEF]+);/chr(hex($1))/eg;
  0         0  
139 0         0 $string =~ s/</
140 0         0 $string =~ s/>/>/g;
141 0         0 $string =~ s/"/"/g;
142 0         0 $string =~ s/'/'/g;
143 0         0 $string =~ s/&/&/g;
144 0         0 return $string;
145             }
146            
147             =head2 C
148            
149             Returns the (quite badly) escaped form of KEY
150            
151             =cut
152            
153            
154             sub XMLKeyescape {
155 36     36 1 39 my $self=shift;
156 36         42 my $string=shift;
157 36         51 $string =~ s/\&/&/g;
158 36         38 $string =~ s/\
159 36         37 $string =~ s/\>/>/g;
160 36         37 $string =~ s/\"/"/g;
161 36         37 $string =~ s/\'/'/g;
162 36         58 $string =~ s/([^[:print:]])/sprintf("&#x%X;",ord($1))/eg;
  0         0  
163 36         78 return $string;
164             }
165            
166             =head2 genID()
167            
168             Returns a new unqiue ID
169            
170             =cut
171            
172             sub genID {
173 136     136 1 152 my $self=shift;
174 136         223 return $self->{ID}++;
175             }
176            
177             =head2 indent(LEVEL)
178            
179             Returns the indentation for LEVEL
180            
181             =cut
182            
183             sub indent {
184 260     260 1 312 my $self=shift;
185 260         246 my $level = shift;
186 260         1222 return $self->{INDENT} x $level;
187             }
188            
189             =head2 packObjectComment(OBJECT)
190            
191             Returns the packed comment of OBJECT
192            
193             =cut
194            
195             sub packObjectComment {
196 68     68 1 78 my $self=shift;
197 68         69 my $data=shift;
198 68 100       172 if ($data->hasComment()) {
199 8         9 my $str = join("\n",@{$data->getComment()});
  8         33  
200 8         15 $str=~s/>/&gt;/g;
201 8         12 $str=~s/-->/-->/g;
202 8         42 return "" ;
203             }
204 60         311 return "";
205             }
206            
207             =head2 packComment(TREE)
208            
209             Returns the packed comment of the object reference by TREE
210            
211             =cut
212            
213             sub packComment {
214 68     68 1 76 my $self=shift;
215 68         70 my $tree=shift;
216 68 100       334 return "" unless $self->{COMMENT}->{$tree};
217 8 50       21 return "" unless ref $self->{COMMENT}->{$tree};
218 8 50       22 return "" unless ref $self->{COMMENT}->{$tree} eq "ARRAY";
219 8         10 my $str = join("\n",@{$self->{COMMENT}->{$tree}});
  8         23  
220 8         12 $str=~s/>/&gt;/g;
221 8         12 $str=~s/-->/-->/g;
222 8         32 return "" ;
223             }
224            
225             =head2 C
226            
227             Returns the ELEMENT for OBJECT
228            
229             =cut
230            
231             sub packElement {
232 20     20 1 27 my $self=shift;
233 20         21 my $elem=shift;
234 20         21 my $data=shift;
235 20         26 my $level=shift;
236 20         20 my $id=shift;
237 20         31 return $self->indent($level)."<$elem id=\"$id\">".$self->packObjectComment($data);
238             }
239             sub packElement2 {
240 34     34 0 36 my $self=shift;
241 34         36 my $elem=shift;
242 34         36 my $data=shift;
243 34         36 my $level=shift;
244 34         34 my $id=shift;
245 34         56 return $self->indent($level)."<$elem id=\"$id\">";
246             }
247            
248             =head2 C
249            
250             Returns the an XML item
251            
252             =cut
253            
254             sub packItem {
255 20     20 1 21 my $self=shift;
256 20         23 my $elem=shift;
257 20         16 my $level=shift;
258 20         20 my $id=shift;
259 20         21 my $tree=shift;
260 20         37 return $self->indent($level)."<$elem id=\"$id\">".$self->packComment($tree)
261             }
262             sub packItem2 {
263 40     40 0 42 my $self=shift;
264 40         42 my $elem=shift;
265 40         39 my $level=shift;
266 40         38 my $id=shift;
267 40         36 my $tree=shift;
268 40         63 return $self->indent($level)."<$elem id=\"$id\">"
269             }
270            
271            
272             =head2 C
273            
274             Converts one Data::Sofu::Object into its XML representation
275            
276             =cut
277            
278             sub packObjectData {
279 66     66 1 84 my $self=shift;
280 66         70 my $data=shift;
281 66         72 my $level=shift;
282 66         103 my $id = $self->genID();
283 66         109 my $r = ref $data;
284             #Maybe call packData on unknown Datastructures..... :)
285 66 50 33     599 die ("Unknown Datastructure, can only work with Arrays and Hashes but not $r") unless $r and $r =~ m/Data::Sofu/ and $r->isa("Data::Sofu::Object");
      33        
286            
287 66         72 my $odata=$data;
288 66 100 66     174 if ($data->isReference() and $data->valid()) {
289 10         26 $data=$data->follow();
290             }
291 66 50       155 if ($data->isReference()) { #Reference to a Reference not yet allowed!
292 0         0 croak("No Reference to a Reference allowed for now!");
293 0         0 return $self->indent($level)."\n".$self->packObjectComment($odata)."\n";
294             }
295 66 100       195 if ($self->{IDS}->{$data}) {
296 10         18 return $self->indent($level)."{IDS}->{$data}\" />".$self->packObjectComment($odata)."\n";
297             }
298 56         136 $self->{IDS}->{$data}=$id;
299 56         109 $self->{IDS}->{$odata}=$id;
300 56 100       140 if ($data->isValue()) {
301 36 100       80 return $self->packElement2("Value",$odata,$level,$id).$self->XMLescape($data->toString(),$level+1)."".$self->packObjectComment($odata)."\n" if $data->toString() ne "";
302 2         5 return $self->indent($level)."".$self->packObjectComment($odata)."\n";
303             }
304 20 100       64 if ($data->isMap()) {
305 6         15 my $str=$self->packElement("Map",$odata,$level,$id)."\n";
306 6         22 foreach my $key ($data->orderedKeys()) {
307 8         26 $str.=$self->indent($level+1)."XMLKeyescape($key)."\">\n";
308 8         27 $str.=$self->packObjectData($data->object($key),$level+2);
309 8         21 $str.=$self->indent($level+1)."\n";
310             }
311 6         18 return $str.$self->indent($level)."\n";
312             }
313 14 100       38 if ($data->isList()) {
314 12         24 my $str=$self->packElement("List",$odata,$level,$id)."\n";
315 12         38 while (my $element = $data->next()) {
316 48         123 $str.=$self->packObjectData($element,$level+1);
317             }
318 12         31 return $str.$self->indent($level)."\n"
319             }
320 2         6 return $self->indent($level)."\n".$self->packObjectComment($odata);
321             }
322            
323             =head2 C
324            
325             Converts one perl structure into its XML representation
326            
327             =cut
328            
329             sub packData {
330 66     66 1 81 my $self=shift;
331 66         80 my $data=shift;
332 66         121 my $level=shift;
333 66         65 my $tree=shift;
334 66         110 my $id = $self->genID();
335 66 100       147 if (ref $data) {
336 22 100       59 if ($self->{IDS}->{$data}) {
337 4         8 return $self->indent($level)."{IDS}->{$data}\" />".$self->packComment($tree)."\n";
338             }
339 18         44 $self->{IDS}->{$data}=$id;
340 18 100       41 if (ref $data eq "HASH") {
341 6         16 my $str=$self->packItem("Map",$level,$id,$tree)."\n";
342 6         14 foreach my $key (sort keys %{$data}) {
  6         23  
343 8         17 $str.=$self->indent($level+1)."XMLKeyescape($key)."\">\n";
344 8         35 $str.=$self->packData($data->{$key},$level+2,$tree."->".Data::Sofu::Sofukeyescape($key));
345 8         22 $str.=$self->indent($level+1)."\n";
346             }
347 6         17 return $str.$self->indent($level)."\n";
348             }
349 12 50       23 if (ref $data eq "ARRAY") {
350 12         21 my $str=$self->packItem("List",$level,$id,$tree)."\n";
351 12         21 my $i=0;
352 12         11 foreach my $element (@{$data}) {
  12         21  
353 48         165 $str.=$self->packData($element,$level+1,$tree."->".$i++);
354             }
355 12         29 return $str.$self->indent($level)."\n"
356             }
357             else {
358 0         0 confess "Can't pack: ",ref $data," @ $tree";
359             }
360             }
361 44 100       81 if (defined ($data)) {
362 42 100       109 return $self->packItem2("Value",$level,$id,$tree).$self->XMLescape($data,$level+1)."".$self->packComment($tree)."\n" if $data ne "";
363 2         7 return $self->indent($level)."".$self->packComment($tree)."\n";
364             }
365 2         5 return $self->indent($level)."\n".$self->packComment($tree);
366             }
367            
368            
369             =head2 C
370            
371             Converts one Data::Sofu::Object into its XML representation
372            
373             =cut
374            
375             sub packObject {
376 2     2 1 3 my $self=shift;
377 2         3 my $data=shift;
378 2         24 my $r = ref $data;
379 2         5 my $header=shift;
380 2   50     10 my $level=int(shift || 0);
381 2 50       8 $level=0 unless $level;
382             #Maybe call packData on unknown Datastructures..... :)
383 2 50 33     28 die ("Unknown Datastructure, can only work with Data::Sofu::Object's but not $r, did you mean pack() ?") unless $r and $r =~ m/Data::Sofu/ and $r->isa("Data::Sofu::Object");
      33        
384 2 50       10 unless ($data->isMap()) {
385 0         0 my $m = new Data::Sofu::Map();
386 0         0 $m->setAttribute("Value",$data);
387 0         0 $data=$m;
388             }
389 2         6 $self->{IDS} = {};
390 2         14 $self->{ID} = 1;
391 2         6 my $id = $self->genID();
392 2         6 $self->{IDS}->{$data}=$id;
393 2         4 my $str="";
394 2 50       8 $str.=qq(\n\n) unless ($header);
395 2 50       5 $str.=$header if $header;
396 2         8 $str.=$self->packElement("Sofu",$data,$level,$id)."\n";
397 2         10 foreach my $key ($data->orderedKeys()) {
398 10         24 $str.=$self->indent($level+1)."XMLKeyescape($key)."\">\n";
399 10         36 $str.=$self->packObjectData($data->object($key),$level+2);
400 10         26 $str.=$self->indent($level+1)."\n";
401             }
402 2         13 return $str.$self->indent($level)."\n";
403             }
404            
405             =head2 pack(TREE,[COMMENTS,[HEADER]])
406            
407             packs TREE to XML using Comments
408            
409             =over
410            
411             =item TREE
412            
413             Perl datastructure to pack. Can be a hash, array or scalar (or array of hashes of hashes of arrays or whatever). Anything NOT a hash will be converted to TREE={Value=>TREE};
414            
415             It can also be a Data::Sofu::Object or derived (Data::Sofu::Map, Data::Sofu::List, Data::Sofu::Value, Data::Sofu::...).
416             Anything not a Data::Sofu::Map will be converted to one (A Map with one attribute called "Value" that holds TREE).
417            
418             =item COMMENTS
419            
420             Comment hash (as returned by Data::Sofu::getSofucomments() or Data::Sofu->new()->comments() after any file was read).
421            
422             These are ignored if TREE is a Data::Sofu::Object or derived. Data::Sofu::Object's store their comments in themselves. See Data::Sofu::Object->importComments() to import them.
423            
424             Can be undef or {}.
425            
426             =back
427            
428             =cut
429            
430             sub pack {
431 4     4 1 7 my $self=shift;
432 4         5 my $data=shift;
433 4         10 my $r = ref $data;
434 4         6 my $comments=shift;
435 4 100       10 $comments = {} unless defined $comments;
436 4 100 66     339 return $self->packObject($data,@_) if $r and $r =~ m/Data::Sofu::/ and $data->isa("Data::Sofu::Object");
      66        
437 2         3 my $header=shift;
438 2 50 33     15 $data = {Value=>$data} unless ref $data and ref $data eq "HASH";
439 2 50 33     18 $data = {Value=>$data} unless ref $data and ref $data eq "HASH";
440             #$self->die("Data format wrong, must be hashref") unless (ref $data and ref $data eq "HASH");
441 2   50     11 my $level=int(shift || 0);
442 2         4 $self->{COMMENT}=$comments;
443 2 50       6 $level=0 unless $level;
444 2         5 $self->{IDS} = {};
445 2         8 $self->{ID} = 1;
446 2         6 my $id = $self->genID();
447 2         6 $self->{IDS}->{$data}=$id;
448 2         5 my $str="";
449 2 50       7 $str.=qq(\n\n) unless ($header);
450 2 50       6 $str.=$header if $header;
451 2         5 $str.=$self->packItem("Sofu",$level,$id,"=")."\n";
452 2         4 foreach my $key (keys %{$data}) {
  2         9  
453 10         22 $str.=$self->indent($level+1)."XMLKeyescape($key)."\">\n";
454 10         37 $str.=$self->packData($data->{$key},$level+2,"->".Data::Sofu::Sofukeyescape($key));
455 10         29 $str.=$self->indent($level+1)."\n";
456             }
457 2         6 return $str.$self->indent($level)."\n";
458            
459             }
460            
461             my @tree = ();
462             my @ids =();
463             my $tree =();
464             my @ref = ();
465             my %id = ();
466             my $ret = "";
467             my @keys = ();
468             my %com = ();
469             my $end=0;
470            
471             =head2 load (STRING)
472            
473             Unpacks a SofuML string to perl datastructures
474            
475             Don't use this, use readSofu("file.xml") instead.
476            
477             =cut
478            
479             sub read {
480 0     0 0   my $self=shift;
481 0           my $data=shift;
482 0           eval {
483 0           local $^W = 0;
484 0           require XML::Parser;
485             };
486 0           @tree = ();
487 0           @ids =();
488 0           $tree =();
489 0           @ref = ();
490 0           %id = ();
491 0           $ret = "";
492 0           @keys = ();
493 0           %com = ();
494 0 0         confess "You will need XML::Parser for reading SofuML files" if ($@);
495             #my $parser =XML::Parser->new(Style=>"Tree");
496             #my $tree=$parser->parse($data);
497             #use Data::Dumper;
498             #print Data::Dumper->Dump([$tree]);
499 0           my $parser =XML::Parser->new(Handlers=>{Start => \&tag_start,End => \&tag_end,Char => \&characters, Comment=>\&comment});
500 0           $parser->parse($data);
501 0           foreach my $e (@ref) {
502 0           my $target = $$$e;
503 0           $$e = undef;
504 0 0         $$e = $id{$target} if $id{$target};
505             }
506             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,\%id,\%com],[qw/@tree $ret %com @ref %id %com/]);
507 0 0         return ($ret,{%com}) if wantarray;
508 0           return $ret;
509             }
510            
511             =head2 load (STRING)
512            
513             Unpacks SofuML string to Data::Sofu::Object's from STRING
514            
515             Don't use this, use readSofu("file.xml") instead.
516            
517             =cut
518            
519             sub load {
520 0     0 1   my $self=shift;
521 0           my $data=shift;
522 0           eval {
523 0           local $^W = 0;
524 0           require XML::Parser;
525             };
526 0           @tree = ();
527 0           @ids =();
528 0           $tree =();
529 0           @ref = ();
530 0           %id = ();
531 0           $ret = "";
532 0           @keys = ();
533 0           %com = ();
534 0 0         confess "You will need XML::Parser for reading SofuML files" if ($@);
535 0           require Data::Sofu::Object;
536             #my $parser =XML::Parser->new(Style=>"Tree");
537             #my $tree=$parser->parse($data);
538             #use Data::Dumper;
539             #print Data::Dumper->Dump([$tree]);
540 0           my $parser =XML::Parser->new(Handlers=>{Start => \&otag_start,End => \&otag_end,Char => \&ocharacters, Comment=>\&ocomment});
541 0           $parser->parse($data);
542 0           foreach my $e (@ref) {
543 0           my $target = $e->follow();
544 0 0         $e->dangle($id{$target}) if $id{$target};
545             }
546             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,\%id],[qw/@tree $ret %com @ref %id/]);
547 0           return $ret;
548             }
549            
550            
551             ## XML Parser Handlers
552             sub tag_start {
553 0     0 0   my $xp=shift;
554 0           my $tag=lc(shift);
555 0           my $key="";
556 0           my $id = -1;
557 0           my $idref="";
558 0           $end=0;
559 0           while (@_) {
560 0           my $k=lc shift;
561 0           my $v=shift;
562 0 0         $id = $v if $k eq "id";
563 0 0         $idref = $v if $k eq "idref";
564 0 0         $key = $v if $k eq "key";
565             }
566 0 0         if ($tag eq "value") {
    0          
    0          
    0          
    0          
    0          
    0          
567 0           push @tree,"";
568 0           push @ids,$id;
569             }
570             elsif ($tag eq "undefined") {
571 0           push @tree,undef;
572 0           push @ids,$id;
573             }
574             elsif ($tag eq "reference") {
575 0           push @tree,\$idref;
576 0           push @ids,-1;
577             }
578             elsif ($tag eq "sofu") {
579 0           push @tree,{};
580 0           push @ids,$id;
581             }
582             elsif ($tag eq "map") {
583 0           push @tree,{};
584 0           push @ids,$id;
585             }
586             elsif ($tag eq "list") {
587 0           push @tree,[];
588 0           push @ids,$id;
589 0           push @keys,0;
590             }
591             elsif ($tag eq "element") {
592 0           push @keys,$key;
593             }
594             else {
595 0           die "Unknown Tag $tag";
596             }
597            
598             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,$tag,$key],[qw/@tree $ret %com @ref $tag $key/]);<>;
599             }
600            
601             sub characters {
602 0     0 0   my $xp=shift;
603 0           my $data=$xp->recognized_string;
604 0 0 0       $tree[-1].= $data unless ref $tree[-1] or not defined $tree[-1]; #Ignore chars in everything but a Value
605             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,$data],[qw/@tree $ret %com @ref $data/]);<>;
606             }
607            
608             sub comment {
609 0     0 0   my $xp=shift;
610 0           my $data=shift;
611 0           $data=~s/^ //g;
612 0           $data=~s/ $//g;
613 0 0         $keys[-1]-- if ($end);
614 0           my $tree=join("->",map{Data::Sofu::Sofukeyescape($_)} @keys);
  0            
615 0 0         $tree="->".$tree if $tree;
616 0 0         $tree="=" unless $tree;
617 0           push @{$com{$tree}},split /\n/,$data;
  0            
618 0 0         $keys[-1]++ if ($end);
619             }
620            
621             sub tag_end {
622 0     0 0   my $xp=shift;
623 0           my $tag=lc(shift);
624             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,$tag],[qw/@tree $ret %com @ref $tag/]);<>;
625 0 0         if ($tag eq "element") {
626 0           my $key = pop @keys;
627 0           $tree[-1]->{$key}=$ret;
628 0 0 0       if (ref $ret and ref $ret eq "SCALAR") {
629 0           push @ref,\$tree[-1]->{$key};
630             }
631 0           return;
632             }
633 0           $ret=pop @tree;
634 0 0 0       $ret = XMLunescape($ret) unless ref $ret or not defined $ret;
635 0 0         pop @keys if ($tag eq "list");
636 0           $id{pop @ids}=$ret;
637 0 0 0       if ($tree[-1] and ref $tree[-1] and ref $tree[-1] eq "ARRAY") {
      0        
638 0           push @{$tree[-1]}, $ret;
  0            
639 0 0 0       if (ref $ret and ref $ret eq "SCALAR") {
640 0           push @ref,\$tree[-1]->[-1];
641             }
642 0           $end = -1;
643 0           $keys[-1]++;
644             }
645             }
646            
647             my $elem = 0;
648            
649             sub otag_start {
650 0     0 0   my $xp=shift;
651 0           my $tag=lc(shift);
652 0           my $key="";
653 0           my $id = -1;
654 0           my $idref="";
655 0           $end=0;
656 0           while (@_) {
657 0           my $k=lc shift;
658 0           my $v=shift;
659 0 0         $id = $v if $k eq "id";
660 0 0         $idref = $v if $k eq "idref";
661 0 0         $key = $v if $k eq "key";
662             }
663 0 0         if ($tag eq "value") {
    0          
    0          
    0          
    0          
    0          
    0          
664 0           push @tree,Data::Sofu::Value->new("");
665 0           push @ids,$id;
666             }
667             elsif ($tag eq "undefined") {
668 0           push @tree,Data::Sofu::Undefined->new();
669 0           push @ids,$id;
670             }
671             elsif ($tag eq "reference") {
672 0           my $r=Data::Sofu::Reference->new($idref);
673 0           push @tree,$r;
674 0           push @ref,$r;
675 0           push @ids,-1;
676             }
677             elsif ($tag eq "sofu") {
678 0           $elem=0;
679 0           push @tree,Data::Sofu::Map->new();
680 0           push @ids,$id;
681             }
682             elsif ($tag eq "map") {
683 0           $elem=0;
684 0           push @tree,Data::Sofu::Map->new();
685 0           push @ids,$id;
686             }
687             elsif ($tag eq "list") {
688 0           push @tree,Data::Sofu::List->new();
689 0           push @ids,$id;
690 0           push @keys,0;
691             }
692             elsif ($tag eq "element") {
693 0           push @keys,$key;
694 0           $elem=1;
695             }
696             else {
697 0           die "Unknown Tag $tag";
698             }
699             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,$tag,$key],[qw/@tree $ret %com @ref $tag $key/]);<>;
700             }
701            
702             sub ocharacters {
703 0     0 0   my $xp=shift;
704 0           my $data=$xp->recognized_string;
705 0 0 0       $tree[-1]->set($tree[-1]->toString().$data) if $tree[-1] and $tree[-1]->isValue(); #Ignore chars in everything but a Value
706             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,$data],[qw/@tree $ret %com @ref $data/]);<>;
707             }
708            
709             sub ocomment {
710 0     0 0   my $xp=shift;
711 0           my $data=shift;
712 0           $data=~s/^ //g;
713 0           $data=~s/ $//g;
714 0 0 0       if ($end or $elem) {
715 0 0         $ret->appendComment([split /\n/,$data]) if $ret;
716             }
717             else {
718 0 0         $tree[-1]->appendComment([split /\n/,$data]) if $tree[-1];
719             }
720             }
721            
722             sub otag_end {
723 0     0 0   my $xp=shift;
724 0           my $tag=lc(shift);
725 0           $end=0;
726             #print Data::Dumper->Dump([\@tree,$ret,\%com,\@ref,$tag],[qw/@tree $ret %com @ref $tag/]);<>;
727 0 0         if ($tag eq "element") {
728 0           my $key = pop @keys;
729 0           $tree[-1]->setAttribute($key,$ret);
730 0           $elem=0;
731 0           return;
732             }
733 0           $ret=pop @tree;
734 0 0         $ret->set(XMLunescape($ret->toString())) if $ret->isValue();
735 0 0         pop @keys if ($tag eq "list");
736 0           $id{pop @ids}=$ret;
737 0 0 0       if ($tree[-1] and $tree[-1]->isList()) {
738 0           $tree[-1]->appendElement($ret);
739 0           $keys[-1]++;
740 0           $end=-1;
741             }
742             }
743            
744            
745            
746             #sub convert { ##Forget it, no comments returned!!!
747             # my $self=shift;
748             # my $tree=shift;
749             # if ($tree[0] eq "Sofu" or $tree[0] eq "Map") {#Don't care if or descripes a Map
750             # my $res = {};
751             # $self->{IDS}->{$tree[1]->{id})}=$res if ($tree[1] and $tree[1]->{id});
752             # while (@{$tree}) {
753             # my $key = shift @{$tree};
754             # my $value = shift @{$tree};
755             # if (lc $key eq "element") {
756             #
757             # }
758             # }
759             # }
760             #
761             #}
762            
763             =head1 BUGS
764            
765             Reading SofuML files need XML::Parser.
766            
767             =head1 See Also
768            
769             L, L, L
770            
771             =cut
772            
773             1;