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 29 29 100.0
total 208 301 69.1


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