File Coverage

blib/lib/Data/Sofu/Object.pm
Criterion Covered Total %
statement 108 159 67.9
branch 46 76 60.5
condition 3 4 75.0
subroutine 22 33 66.6
pod 27 29 93.1
total 206 301 68.4


line stmt bran cond sub pod time code
1             ###############################################################################
2             #Object.pm
3             #Last Change: 2006-11-01
4             #Copyright (c) 2006 Marc-Seabstian "Maluku" Lucksch
5             #Version 0.28
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::Object - Sofud compatibility layer.
21              
22             =head1 DESCRIPTION
23              
24             Provides a interface similar to the original SofuD (sofu.sf.net)
25              
26             =head1 Synopsis
27              
28             require Data::Sofu::Object;
29             my $map = Data::Sofu::Object->new({Text=>"Hello World"});
30             print ref $map; # Data::Sofu::Map;
31             $map->write(\*STDOUT); # Text = "Hello World"
32             $map->write("file.sofu"); # Text = "Hello World"
33             #You don't need Data::Sofu::Object:
34             use Data::Sofu;
35             $map = loadSofu("file.sofu");
36             $map->write(\*STDOUT);
37              
38             =head1 SYNTAX
39              
40             This Module is pure OO, exports nothing
41              
42             =cut
43              
44             package Data::Sofu::Object;
45              
46 3     3   57 use strict;
  3         7  
  3         111  
47 3     3   14 use warnings;
  3         1956  
  3         3128  
48             require Data::Sofu::Map;
49             require Data::Sofu::List;
50             require Data::Sofu::Value;
51             require Data::Sofu::Undefined;
52             require Data::Sofu::Reference;
53             our $VERSION="0.29";
54             my %seen;
55             our %OBJ;
56             my $indent = "\t";
57              
58 3     3   27 use Carp qw/confess/;
  3         169  
  3         12933  
59              
60             =head1 METHODS
61              
62             C is the base class for C, C, C, C and C.
63              
64             All Methods in here might be overwritten, but work the same way
65              
66             =head2 new(DATA)
67             Creates a new C and returns it
68              
69             Converts DATA to appropriate Objects
70              
71             B
72              
73             There is no need to call C without DATA.
74              
75             =cut
76              
77             sub new {
78 327     327 1 843 my $self={};
79 327         35573 bless $self,shift;
80 327 50       703 if (@_) {
81             #print "@_\n";
82 327         387 my $o = shift;
83 327 100       1942 if (ref($o) eq "HASH") {
    100          
    50          
    100          
84 12 100       29 if (not $seen{$o}) {
85             #confess "BOXXX";
86 8         39 $seen{$o}=Data::Sofu::Map->new();
87 8         28 $seen{$o}->set($o);
88 8         28 return $seen{$o};
89             }
90             else {
91 4         27 return Data::Sofu::Reference->new($seen{$o});
92             }
93             }
94             elsif (ref($o) eq "ARRAY") {
95 12 50       27 if (not $seen{$o}) {
96             #confess "BOXXX";
97 12         44 $seen{$o}= Data::Sofu::List->new();
98 12         38 $seen{$o}->set($o);
99 12         45 return $seen{$o};
100             }
101             else {
102 0         0 return Data::Sofu::Reference->new($seen{$o});
103             }
104             }
105             elsif (ref($o) eq "SCALAR") {
106             #confess "BOXXX";
107 0         0 return Data::Sofu::Value->new($$o);
108             }
109             elsif (ref($o)) {
110 259         1318 return $o;
111             }
112             else {
113             #confess "BOXXX";
114 44 100       158 return Data::Sofu::Value->new($o) if defined $o;
115 2         17 return Data::Sofu::Undefined->new()
116             }
117              
118             }
119 0         0 return $self;
120             }
121             =head2 indent(LEVEL)
122              
123             Internal Function to create indentation during write()
124              
125             LEVEL is the amount of indentation requested
126              
127             Returns Indentation x LEVEL as a string
128              
129             =cut
130              
131             sub indent {
132 109     109 0 161 my $self=shift;
133 109         147 my $l=shift;
134 109 100       241 return "" unless $l;
135 99 100       274 return "" if $l < 0;
136 66         232 return $indent x $l;
137             }
138              
139             =head2 setIndent([NewIndent])
140              
141             Allows different indentations to be used (default is "\t")
142              
143             Returns the current indentation
144              
145             =cut
146              
147             sub setIndent {
148 0     0 1 0 my $self=shift;
149 0 0       0 if (@_) {
150 0         0 $indent=shift;
151             }
152 0         0 return $indent;
153             }
154              
155             =head2 clear()
156              
157             Clears the Buffer of seen Objects only used during the old C and C
158              
159             =cut
160              
161             sub clear {
162 2     2 1 16 %seen=();
163             }
164              
165             =head2 asValue()
166              
167             Returns the Object as a C>Data::Sofu::Value> or throws an error if it can't be converted
168              
169             =cut
170              
171             sub asValue {
172 0     0 1 0 confess "Object assumed to be a Value, but it is ".ref shift;
173 0         0 return;
174             }
175              
176             =head2 asList()
177              
178             Returns the Object as a C or throws an error if it can't be converted
179              
180             =cut
181              
182             sub asList {
183 0     0 1 0 confess "Object assumed to be a List, but it is ".ref shift;
184 0         0 return;
185             }
186              
187             =head2 asMap()
188              
189             Returns the Object as a C or throws an error if it can't be converted
190              
191             =cut
192              
193             sub asMap {
194 0     0 1 0 confess "Object assumed to be a Map, but it is ".ref shift;
195 0         0 return;
196             }
197              
198             =head2 asReference()
199              
200             Returns the Object as a C or throws an error if it can't be converted
201              
202             =cut
203              
204             sub asReference {
205 0     0 1 0 confess "Object assumed to be a Reference, but it is ".ref shift;
206 0         0 return;
207             }
208              
209             =head2 C
210              
211             Returns a string representation of the Object, used during write(), should not be called alone
212              
213             LEVEL is the current indentation level.
214              
215             TREE is the current position in the TREE (used for Reference building)
216              
217             =cut
218              
219             sub stringify {
220 0     0 1 0 confess "Error can't stringify an Object which is nothing but an Object";
221             }
222              
223             =head2 C
224              
225             Returns a binary representation of the Object, used during writeBinary(), should never be called alone.
226              
227             TREE is the current position in the TREE (used for Reference building)
228              
229             BINARY DRIVER is a C instance which is initialized with a Encoding, ByteOrder and Sofumark properties.
230              
231             =cut
232              
233             sub binarify {
234 0     0 1 0 confess "Error can't binarify an Object which is nothing but an Object";
235             }
236              
237             =head2 C
238              
239             Recursivly stores a comment identified by TREE, is used to store a single comment of the hash returned by C;
240              
241             =cut
242              
243             sub storeComment {
244 4     4 1 7 my $self=shift;
245 4         6 my $tree=shift;
246 4         4 my $comment=shift;
247 4         21 $self->{Comment}=$comment;
248              
249             }
250              
251             =head2 importComments(COMMENTS)
252              
253             Takes a Hashref (as returned by C and gives every Object its fitting Comment
254              
255             COMMENTS is a reference to a Hash
256              
257             Normally Data::Sofu->new->toObjects($data,$comments) should have done this.
258              
259             =cut
260              
261             sub importComments {
262 0     0 1 0 my $self=shift;
263 0         0 my $comment=shift;
264 0         0 foreach my $key (keys %$comment) {
265 0         0 my $wkey=$key;
266 0         0 $wkey=~s/^->//;
267 0 0       0 $wkey="" if $key eq "=";
268 0         0 $self->storeComment($wkey,$comment->{$key});
269             }
270            
271             }
272              
273             =head2 isValue()
274              
275             Return 1 if this Object is a C instance, 0 otherwise.
276              
277             =cut
278              
279             sub isValue {
280 280     280 1 968 return 0;
281             }
282              
283             =head2 isList()
284              
285             Return 1 if this Object is a C instance, 0 otherwise.
286              
287             =cut
288              
289             sub isList {
290 28     28 1 92 return 0;
291             }
292              
293             =head2 isMap()
294              
295             Return 1 if this Object is a C instance, 0 otherwise.
296              
297             =cut
298              
299             sub isMap {
300 196     196 1 601 return 0;
301             }
302              
303             =head2 stringComment()
304              
305             Returns the current Objects comment as a string (inculding the # sign)
306              
307             =cut
308              
309             sub stringComment {
310 102     102 1 140 my $self=shift;
311 102 100       266 return " #".join("\n#",@{$self->{Comment}}) if $self->{Comment};
  12         95  
312 90         483 return "";
313             }
314              
315             =head2 getComment()
316              
317             Returns the current comment as an arrayref (One string for each line)
318              
319             =cut
320             sub getComment {
321 907     907 1 1302 my $self=shift;
322 907         3504 return $self->{Comment};
323             }
324              
325             =head2 hasComment()
326              
327             Returns the amount of comment lines.
328              
329             =cut
330              
331             sub hasComment {
332 80     80 1 103 my $self=shift;
333 80 100       332 return 0 unless $self->{Comment};
334 20         131 return scalar @{$self->{Comment}};
  20         97  
335             }
336              
337             =head2 setComment(COMMENT)
338            
339             Sets the comments for this Object.
340              
341             COMMENT should be a reference to an Array
342              
343             =cut
344              
345             sub setComment {
346 140     140 1 254 my $self=shift;
347 140         177 my $c = shift;
348 140         237 delete $self->{Comment};
349 140 50       3244 next unless $c;
350 140 50       518 if (ref $c) {
351 140 50       333 if (ref $c eq "ARRAY") {
352 140         627 $self->{Comment}=$c;
353             }
354             else {
355 0         0 die "Unknown Comment Format, has to be an Arrayref or Scalar";
356             }
357             }
358             else {
359 0         0 $self->{Comment}=[$c,@_];
360             }
361             }
362              
363             =head2 appendComment(COMMENT)
364              
365             Appends to the comments for this Object.
366              
367             COMMENT should be a reference to an Array
368              
369             =cut
370              
371             sub appendComment {
372 0     0 1 0 my $self=shift;
373 0         0 my $c = shift;
374 0 0       0 $self->{Comment}=[] unless $self->{Comment};
375 0 0       0 next unless $c;
376 0 0       0 if (ref $c) {
377 0 0       0 if (ref $c eq "ARRAY") {
378 0         0 push @{$self->{Comment}},@{$c};
  0         0  
  0         0  
379             }
380             else {
381 0         0 die "Unknown Comment Format, has to be an Arrayref or Scalar";
382             }
383             }
384             else {
385 0         0 push @{$self->{Comment}},$c,@_;
  0         0  
386             }
387             }
388              
389             =head2 isDefined()
390              
391             Returns 1 if the Object is not an instance of C
392              
393             =cut
394              
395             sub isDefined {
396 0     0 1 0 return 1;
397             }
398              
399             =head2 isReference()
400              
401             Returns 0 if the Object is not an instance of C
402              
403             =cut
404              
405             sub isReference {
406 1966     1966 1 8504 return 0;
407             }
408              
409             =head2 pack()
410              
411             Returns a string representation of the current Object and all Objects it might include
412              
413             =cut
414              
415             sub pack {
416 1     1 1 2 my $self=shift;
417 1         25 %OBJ=();
418 1         7 return $self->string(-1,"");
419             #confess "You can only Pack Maps";
420             }
421              
422             =head2 binaryPack()
423              
424             Returns a string of that represents the current Object according the the Binary Sofu specification.
425              
426             Only works on C's other Objects are getting boxed in a Map
427              
428             =cut
429              
430             sub binaryPack {
431 0     0 1 0 my $x = new Data::Sofu::Map;
432 0         0 $x->setAttribute("Value",shift); #$self
433 0         0 %OBJ=($x=>"->");
434 0         0 $x->binaryPack(@_);
435             }
436              
437             =head2 C
438              
439             A helper function to detect multiple references and convert them to Sofu References, calls stringify with its arguments
440              
441             $o->string(-1,"") === $o->pack();
442             print $map->string(0,"") === $o->write(\*STDOUT);
443              
444             =cut
445              
446             sub string { #Helper function to detect multiple References
447 102     102 1 151 my $self=shift;
448 102         118 my $level=shift;
449 102         124 my $tree=shift;
450 102         135 my $oself=$self;
451 102 100       288 if ($self->isReference()) {
452 12 50       48 if ($self->valid()) {
453 12         43 $self=$self->follow();
454             }
455             else {
456             #confess ($self->follow());
457 0         0 return "@".$self->follow().$self->stringComment()."\n";
458             }
459             }
460 102 100       521 if ($OBJ{$self}) {
461 12         62 return "@".$OBJ{$self}.$oself->stringComment()."\n";
462             }
463 90   100     353 $OBJ{$self}=$tree || "->";
464 90         363 return $self->stringify($level,$tree);
465             }
466              
467             =head2 C
468              
469             Returns the Objects Comments packed by a BINARY DRIVER, used by binaryPack() and writeBinary()
470              
471             Never call this one alone.
472              
473             =cut
474              
475             sub packComment {
476 204     204 1 266 my $self=shift;
477 204         274 my $bin=shift;
478 204 100       3087 return $bin->packText("") unless $self->{Comment};
479 24         36 return $bin->packText(join("\n",@{$self->{Comment}}));
  24         133  
480             }
481              
482              
483             =head2 C
484              
485             A helper function to detect multiple references and convert them to Sofu References, calls stringify with its arguments. Should never be called alone, because the result will miss its header.
486              
487             =cut
488             sub binary { #Helper function to detect multiple References
489 198     198 1 5821 my $self=shift;
490 198         913 my $tree=shift;
491 198         3391 my $bin=shift;
492 198         1496 my $oself=$self;
493 198 100       488 if ($self->isReference()) {
494 30 50       106 if ($self->valid()) {
495 30         95 $self=$self->follow();
496             }
497             else {
498 0         0 return $bin->packType(4).$self->packComment($bin).$bin->packText("@".$self->follow());
499             }
500             }
501 198 100       602 if ($OBJ{$self}) {
502 30         81 return $bin->packType(4).$oself->packComment($bin).$bin->packText("@".$OBJ{$self});
503             }
504 168   50     998 $OBJ{$self}=$tree || "->";
505 168         2729 return $self->binarify($tree,$bin);
506             }
507              
508             =head2 write(FILE)
509              
510             Writes the string representation of this Object to a file
511              
512             File can be:
513              
514             A filename,
515              
516             a filehandle or
517              
518             a reference to a scalar.
519              
520             =cut
521              
522             sub write {
523 2     2 1 267 my $self=shift;
524 2         4 my $file=shift;
525 2         5 my $fh;
526 2         39 %OBJ=();
527 2 100       15 unless (ref $file) {
    50          
    0          
528 1 50       102 open $fh,">:raw:encoding(UTF-16)",$file or die "Sofu error open: $$self{CurFile} file: $!";
529             }
530             elsif (ref $file eq "SCALAR") {
531 1         8 utf8::upgrade($$file);
532 1 50   1   15 open $fh,">:utf8",$file or die "Can't open perlIO: $!";
  1         4  
  1         12  
  1         101  
533             }
534             elsif (ref $file eq "GLOB") {
535 0         0 $fh=$file;
536             }
537             else {
538 0         0 $self->warn("The argument to load or loadfile has to be a filename, reference to a scalar or filehandle");
539 0         0 return;
540             }
541 2         2254 print $fh $self->string(0,"");
542             #$fh goes out of scope here!
543             }
544             =head2 C
545              
546             Writes the binary representation of this Object to a file
547              
548             File can be:
549              
550             A filename,
551              
552             a filehandle or
553              
554             a reference to a scalar.
555              
556             Note: the filehandle will be set to binmode
557              
558             Uses C as driver.
559              
560             =cut
561              
562             sub writeBinary {
563 4     4 0 13 my $self=shift;
564 4         8 my $file=shift;
565 4         10 my $fh;
566 4         89 %OBJ=($self=>"->");
567 4 100       25 unless (ref $file) {
    50          
    0          
568 2 50       368 open $fh,">:raw",$file or die "Sofu error open: $$self{CurFile} file: $!";
569             }
570             elsif (ref $file eq "SCALAR") {
571 2 50       81 open $fh,">",$file or die "Can't open perlIO: $!";
572             }
573             elsif (ref $file eq "GLOB") {
574 0         0 $fh=$file;
575             }
576             else {
577 0         0 $self->warn("The argument to load or loadfile has to be a filename, reference to a scalar or filehandle");
578 0         0 return;
579             }
580 4         18 binmode $fh;
581 4         32 print $fh $self->binaryPack(@_);
582             #$fh goes out of scope here!
583             }
584              
585             =head1 BUGS
586              
587             Comment and Binary Modes are not really sofud complient, might change in the future
588              
589             =head1 SEE ALSO
590              
591             L, L, L, L, L, L, L
592              
593             =cut
594              
595             1;