File Coverage

blib/lib/Eobj/PLroot.pl
Criterion Covered Total %
statement 63 272 23.1
branch 17 118 14.4
condition 2 18 11.1
subroutine 8 24 33.3
pod n/a
total 90 432 20.8


line stmt bran cond sub pod time code
1             #
2             # This file is part of the Eobj project.
3             #
4             # Copyright (C) 2003, Eli Billauer
5             #
6             # This program is free software; you can redistribute it and/or modify
7             # it under the terms of the GNU General Public License as published by
8             # the Free Software Foundation; either version 2 of the License, or
9             # (at your option) any later version.
10             #
11             # This program is distributed in the hope that it will be useful,
12             # but WITHOUT ANY WARRANTY; without even the implied warranty of
13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14             # GNU General Public License for more details.
15             #
16             # You should have received a copy of the GNU General Public License
17             # along with this program; if not, write to the Free Software
18             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19             #
20             # A copy of the license can be found in a file named "licence.txt", at the
21             # root directory of this project.
22             #
23              
24             # Eobj's basic root class
25             ${__PACKAGE__.'::errorcrawl'}='system';
26             #our $errorcrawl='system';
27             sub new {
28 2     2   3 my $this = shift;
29 2         24 my $self = $this->SUPER::new(@_);
30 2   33     12 my $class = ref($this) || $this;
31 2 50       7 $self = {} unless ref($self);
32 2         5 bless $self, $class;
33 2         14 $self->store_hash([], @_);
34              
35 2         11 my $name = $self->get('name');
36              
37 2 50       12 if (defined $name) {
38 2 50       11 puke("New \'$class\' object created with illegal name: ".$self->prettyval($name)."\n")
39             unless ($name=~/^[a-zA-Z_]\w*$/);
40              
41 2 50       6 blow("New \'$class\' object created with an already occupied name: \'$name\'\n")
42             if (exists $Eobj::objects{$name});
43 2         4 my $lc = lc($name);
44 2         6 foreach (keys %Eobj::objects) {
45 1 50       5 blow("New \'$class\' object created with a name \'$name\' when \'$_\' is already in the system (only case difference)\n")
46             if (lc($_) eq $lc);
47             }
48             } else {
49             # No name given? Let's be forgiving, and give one of our own...
50 0         0 $name = $self->suggestname('DefaultName');
51 0         0 $self->const('name', $name);
52             }
53 2         6 $Eobj::objects{$name}=$self;
54              
55 2         104 $self -> const('eobj-object-count', $Eobj::objectcounter++);
56 2         6 return $self;
57             }
58              
59             sub destroy {
60 2     2   3 my $self = shift;
61 2         4 my $name = $self->get('name');
62              
63 2         5 delete $Eobj::objects{$name};
64 2         5 bless $self, 'PL_destroyed';
65 2         2 undef %{$self};
  2         19  
66              
67 2         12 return undef;
68             }
69              
70 2     2   3 sub survivor { } # So method is recognized
71              
72             sub who {
73 0     0   0 my $self = shift;
74 0         0 return "object \'".$self->get('name')."\'";
75             }
76              
77             sub safewho {
78 0     0   0 my ($self, $who) = @_;
79 0 0       0 return "(non-object item)" unless ($self->isobject($who));
80 0         0 return $who->who;
81             }
82              
83             sub isobject {
84 0     0   0 my ($self, $other) = @_;
85 0         0 my $r = ref $other;
86 0 0       0 return 1 if (Eobj::definedclass($r) == 2);
87 0         0 return undef;
88             }
89              
90             sub objbyname {
91 0     0   0 my ($junk, $name) = @_;
92 0         0 return $Eobj::objects{$name};
93             }
94              
95             sub suggestname {
96 0     0   0 my ($self, $name) = @_;
97 0         0 my $sug = $name;
98 0         0 my ($bulk, $num) = ($name =~ /^(.*)_(\d+)$/);
99 0         0 my %v;
100              
101 0         0 foreach (keys %Eobj::objects) { $v{lc($_)}=1; } # Store lowercased names
  0         0  
102 0 0       0 unless (defined $bulk) {
103 0         0 $bulk = $name;
104 0         0 $num = 0;
105             }
106            
107 0         0 while ($v{lc($sug)}) {
108 0         0 $num++;
109 0         0 $sug = $bulk.'_'.$num;
110             }
111 0         0 return $sug;
112             }
113              
114             sub get {
115 7     7   10 my $self = shift;
116 7         7 my $prop = shift;
117 7         8 my $final;
118              
119 7 50       16 my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0         0  
120              
121 7         16 $final = $self->{join("\n", 'plPROP', @path)};
122              
123             # Now try to return it the right way. If we have a reference, then
124             # the property is set. So if the calling context wants an array, why
125             # hassle? Let's just give an array.
126             # But if a scalar is expected, and we happen to have only one
127             # member in the list -- let's be kind and give the first value
128             # as a scalar.
129              
130 7 50       15 if (ref($final)) {
131 7 50       25 return @{$final} if (wantarray);
  0         0  
132 7         7 return ${$final}[0];
  7         24  
133             }
134              
135             # We got here, so the property wasn't defined. Now, if
136             # we return an undef in an array context, it's no good, because it
137             # will be considered as a list with lenght 1. If the property
138             # wasn't defined we want to say "nothing" -- and that's an empty list.
139              
140 0 0       0 return () if (wantarray);
141              
142             # Wanted a scalar? Undef is all we can offer now.
143              
144 0         0 return undef;
145             }
146              
147             sub getraw {
148 8     8   11 my $self = shift;
149            
150 8         30 return $self->{join("\n", 'plPROP', @_)};
151             }
152              
153             sub store_hash {
154 2     2   2 my $self = shift;
155 2         3 my $rpath = shift;
156 2         3 my @path = @{$rpath};
  2         5  
157 2         7 my %h = @_;
158              
159 2         6 foreach (keys %h) {
160 2         3 my $val = $h{$_};
161              
162 2 50       9 if (ref($val) eq 'HASH') {
    50          
163 0         0 $self->store_hash([@path, $_], %{$val});
  0         0  
164             } elsif (ref($val) eq 'ARRAY') {
165 0         0 $self->const([@path, $_], @{$val});
  0         0  
166             } else {
167 2         12 $self->const([@path, $_], $val);
168             }
169             }
170             }
171              
172             sub const {
173 4     4   6 my $self = shift;
174 4         6 my $prop = shift;
175              
176 4 100       9 my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  2         5  
177              
178 4         11 my @newval = @_;
179              
180 4         13 my $pre = $self->getraw(@path);
181              
182 4 50       10 if (defined($pre)) {
183 0 0       0 puke("Attempt to change a settable property into constant\n")
184             unless (ref($pre) eq 'PL_const');
185              
186 0         0 my @pre = @{$pre};
  0         0  
187              
188 0         0 my $areeq = ($#pre == $#newval);
189 0         0 my $i;
190 0         0 my $eq = $self->get(['plEQ',@path]);
191              
192 0 0       0 if (ref($eq) eq 'CODE') {
193 0         0 for ($i=0; $i<=$#pre; $i++) {
194 0 0       0 $areeq = 0 unless (&{$eq}($pre[$i], $newval[$i]));
  0         0  
195             }
196             } else {
197 0         0 for ($i=0; $i<=$#pre; $i++) {
198 0 0       0 $areeq = 0 unless ($pre[$i] eq $newval[$i]);
199             }
200             }
201              
202 0 0       0 unless ($areeq) {
203 0 0 0     0 if (($#path==2) && ($path[0] eq 'vars') && ($path[2] eq 'dim')) {
      0        
204             # This is dimension inconsintency. Will happen a lot to novices,
205             # and deserves a special error message.
206 0         0 wrong("Conflict in setting the size of variable \'$path[1]\' in ".
207             $self->who.". The conflicting values are ".
208             $self->prettyval(@pre)." and ".$self->prettyval(@newval).
209             ". (This usually happens as a result of connecting variables of".
210             " different sizes, possibly indirectly)\n");
211            
212            
213             } else {
214 0         0 { local $@; require Eobj::PLerrsys; } # XXX fix require to not clear $@?
  0         0  
  0         0  
215 0         0 my ($at, $hint) = &Eobj::PLerror::constdump();
216            
217 0         0 wrong("Attempt to change constant value of \'".
218             join(",",@path)."\' to another unequal value ".
219             "on ".$self->who." $at\n".
220             "Previous value was ".$self->prettyval(@pre).
221             " and the new value is ".$self->prettyval(@newval)."\n$hint\n");
222             }
223             }
224             } else {
225 4 50       8 if ($Eobj::callbacksdepth) {
226 0         0 my $prop = join ",",@path;
227 0         0 my $who = $self->who;
228 0         0 hint("On $who: \'$prop\' = ".$self->prettyval(@newval)." due to magic property setting\n");
229             }
230 4         20 $self->domutate((bless \@newval, 'PL_const'), @path);
231              
232 4         8 my $cbref = $self->getraw('plMAGICS', @path);
233 4 50       20 return unless (ref($cbref) eq 'PL_settable');
234 0         0 my $subref;
235              
236 0         0 $Eobj::callbacksdepth++;
237 0         0 while (ref($subref=shift @{$cbref}) eq 'CODE') {
  0         0  
238 0         0 &{$subref}($self, @path);
  0         0  
239             }
240 0         0 $Eobj::callbacksdepth--;
241             }
242             }
243              
244             sub set {
245 0     0   0 my $self = shift;
246 0         0 my $prop = shift;
247              
248 0         0 my @path;
249 0 0       0 @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0         0  
250              
251 0         0 my @newval = @_;
252              
253 0         0 my $pre = $self->getraw(@path);
254 0         0 my $ppp = ref($pre);
255 0 0 0     0 puke ("Attempted to set a constant property\n")
256             if ((defined $pre) && ($ppp ne 'PL_settable'));
257 0         0 $self->domutate((bless \@newval, 'PL_settable'), @path);
258 0         0 return 1;
259             }
260              
261             sub domutate {
262 4     4   6 my $self = shift;
263 4         4 my $newval = shift;
264 4         5 my $def = 0;
265 4 50 33     5 $def=1 if ((defined ${$newval}[0]) || ($#{$newval}>0));
  4         13  
  0         0  
266            
267 4 50       7 if ($def) {
268 4         12 $self->{join("\n", 'plPROP', @_)} = $newval;
269 0         0 } else { delete $self->{join("\n", 'plPROP', @_)}; }
270 4         7 return 1;
271             }
272              
273             sub seteq {
274 0     0     my $self = shift;
275 0           my $prop = shift;
276 0 0         my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0            
277 0           my $eq = shift;
278 0 0         puke("Callbacks should be references to subroutines\n")
279             unless (ref($eq) eq 'CODE');
280 0           $self->set(['plEQ', @path], $eq);
281             }
282              
283             sub addmagic {
284 0     0     my $self = shift;
285 0           my $prop = shift;
286 0 0         my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0            
287 0           my $callback = shift;
288              
289 0 0         unless (defined($self->get([@path]))) {
290 0           $self->punshift(['plMAGICS', @path], $callback);
291             } else {
292 0           $Eobj::callbacksdepth++;
293 0           &{$callback}($self, @path);
  0            
294 0           $Eobj::callbacksdepth--;
295             }
296             }
297              
298             sub pshift {
299 0     0     my $self = shift;
300 0           my $prop = shift;
301 0 0         my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0            
302 0           my $pre = $self->getraw(@path);
303 0 0         if (ref($pre) eq 'PL_settable') {
304 0           return shift @{$pre};
  0            
305             } else {
306 0 0         return $self->set($prop, undef) # We're changing a constant property here. Will puke.
307             if (defined $pre);
308 0           return undef; # There was nothing there.
309             }
310             }
311              
312             sub ppop {
313 0     0     my $self = shift;
314 0           my $prop = shift;
315 0 0         my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0            
316 0           my $pre = $self->getraw(@path);
317 0 0         if (ref($pre) eq 'PL_settable') {
318 0           return pop @{$pre};
  0            
319             } else {
320 0 0         return $self->set($prop, undef) # We're changing a constant property here. Will puke.
321             if (defined $pre);
322 0           return undef; # There was nothing there.
323             }
324             }
325              
326             sub punshift {
327 0     0     my $self = shift;
328 0           my $prop = shift;
329 0 0         my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0            
330            
331 0           my @val = @_;
332              
333 0           my $pre = $self->getraw(@path);
334 0 0         if (ref($pre) eq 'PL_settable') {
335 0           unshift @{$pre}, @val;
  0            
336             } else {
337 0 0         $self->set(\@path, (defined($pre))? ($pre, @val) : @val);
338             }
339             }
340              
341             sub ppush {
342 0     0     my $self = shift;
343 0           my $prop = shift;
344 0 0         my @path = (ref($prop) eq 'ARRAY') ? @{$prop} : ($prop);
  0            
345            
346 0           my @val = @_;
347              
348 0           my $pre = $self->getraw(@path);
349 0 0         if (ref($pre) eq 'PL_settable') {
350 0           push @{$pre}, @val;
  0            
351             } else {
352 0 0         $self->set(\@path, (defined($pre))? (@val, $pre) : @val);
353             }
354             }
355              
356             sub globalobj {
357 0     0     return &Eobj::globalobj();
358             }
359              
360             sub linebreak {
361 0     0     my $self = shift;
362 0           return &Eobj::linebreak(@_);
363             }
364              
365             sub objdump {
366 0     0     my $self = shift;
367 0           my @todump;
368              
369 0 0         unless (@_) {
370 0           @todump = sort {$Eobj::objects{$a}->get('eobj-object-count') <=>
  0            
371             $Eobj::objects{$b}->get('eobj-object-count')}
372             keys %Eobj::objects;
373 0           @todump = map {$Eobj::objects{$_}} @todump;
  0            
374             } else {
375 0           @todump = (@_);
376             }
377              
378 0           foreach my $obj (@todump) {
379 0 0         unless ($self->isobject($obj)) {
380 0           my $r = $Eobj::objects{$obj};
381 0 0         if (defined $r) {
382 0           $obj = $r;
383             } else {
384 0           print "Unknown object specifier ".$self->prettyval($obj)."\n\n";
385 0           next;
386             }
387             }
388            
389 0           my @prefix = ();
390 0           print $self->linebreak($self->safewho($obj).", class=\'".ref($obj)."\':")."\n";
391 0           my $indent = ' ';
392 0           foreach my $prop (sort keys %$obj) {
393 0           my @path = split("\n", $prop);
394 0 0         shift @path if ($path[0] eq 'plPROP');
395 0           my $propname = pop @path;
396              
397             # Now we make sure that the @path will be exactly like @prefix
398             # First, we shorten @prefix if it's longer than @path, or if it
399             # has items that are unequal to @path.
400              
401 0           CHOP: while (1) {
402             # If @prefix is longer, no need to check -- we need chopping
403             # anyhow
404 0 0         unless ($#path < $#prefix) {
405 0           my $i;
406 0           my $last = 1;
407 0           for ($i=0; $i<=$#prefix; $i++) {
408 0 0         if ($prefix[$i] ne $path[$i]) {
409 0           $last = 0; last;
  0            
410             }
411             }
412 0 0         last CHOP if $last;
413             }
414 0           my $tokill = pop @prefix;
415 0           $indent = substr($indent, 0, -((length($tokill) + 3)));
416             }
417              
418 0           my $out = $indent;
419              
420             # And now we fill in the missing @path to @prefix
421 0           while ($#path > $#prefix) {
422 0           my $toadd = $path[$#prefix + 1];
423 0           push @prefix, $toadd;
424 0           $out .= "$toadd > ";
425 0           $toadd =~ s/./ /g; # Substitute any character with white space...
426 0           $indent .= "$toadd ";
427             }
428 0           $out .= "$propname=";
429              
430             # Now we pretty-print the value.
431 0           my $valref = $obj->{$prop};
432 0 0         my @val = (ref($valref)) ? @$valref : (undef);
433            
434 0           my $extraindent = $out;
435 0           $extraindent =~ s/./ /g;
436              
437 0           $out .= $self->prettyval(@val);
438              
439             # Finally, we do some linebreaking, so that the output will be neat
440 0           print $self->linebreak($out, $extraindent)."\n";
441             }
442 0           print "\n";
443             }
444             }
445              
446             sub prettyval {
447 0     0     my $self = shift;
448 0           my $MaxListToPrint = 4;
449 0           my $MaxStrLen = 40;
450              
451 0           my @a = @_; # @a will be manipulated. Get a local copy
452              
453 0 0         if (@a > $MaxListToPrint) {
454             # cap the length of $#a and set the last element to '...'
455 0           $#a = $MaxListToPrint;
456 0           $a[$#a] = "...";
457             }
458 0           for (@a) {
459             # set args to the string "undef" if undefined
460 0 0         $_ = "undef", next unless defined $_;
461 0 0         if (ref $_) {
462 0 0         if ($Eobj::classes{ref($_)}) { # Is this a known object?
463 0           $_='{'.$_->who.'}'; # Get the object's pretty ID
464 0           next;
465             }
466             # force reference to string representation
467 0           $_ .= '';
468 0           s/'/\\'/g;
469             }
470             else {
471 0           s/'/\\'/g;
472             # terminate the string early with '...' if too long
473 0 0 0       substr($_,$MaxStrLen) = '...'
474             if $MaxStrLen and $MaxStrLen < length;
475             }
476             # 'quote' arg unless it looks like a number
477 0 0         $_ = "'$_'" unless /^-?[\d.]+$/;
478             # print high-end chars as 'M-'
479 0           s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
  0            
480             # print remaining control chars as ^
481 0           s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
  0            
482             }
483            
484             # append 'all', 'the', 'data' to the $sub string
485 0 0         return ($#a != 0) ? '(' . join(', ', @a) . ')' : $a[0];
486             }