File Coverage

blib/lib/Games/3D/Template.pm
Criterion Covered Total %
statement 101 124 81.4
branch 37 60 61.6
condition 6 14 42.8
subroutine 13 14 92.8
pod 11 12 91.6
total 168 224 75.0


line stmt bran cond sub pod time code
1              
2             # Template - describe an object class and it's keys/settings
3              
4             package Games::3D::Template;
5              
6             # (C) by Tels
7              
8 2     2   51232 use strict;
  2         5  
  2         85  
9              
10             require Exporter;
11 2     2   10 use vars qw/@ISA $VERSION/;
  2         4  
  2         11268  
12             @ISA = qw/Exporter/;
13              
14             $VERSION = '0.02';
15              
16             ##############################################################################
17             # protected vars
18              
19             # Templates have their own unique IDs
20              
21             {
22             my $id = 1;
23 12     12 0 39 sub ID { return $id++;}
24             }
25              
26             ##############################################################################
27             # methods
28              
29             sub new
30             {
31             # create a new Template
32 12     12 1 1097 my $class = shift;
33              
34 12         44 my $args = $_[0];
35 12 50       42 $args = { @_ } unless ref($_[0]) eq 'HASH';
36              
37 12         33 my $self = { id => ID() };
38 12         31 bless $self, $class;
39              
40 12         138 $self->{valid} = {
41             name => 'STR=',
42             id => 'INT=',
43             state => 'INT=0',
44             state_0 => 'ARRAY=1',
45             state_1 => 'ARRAY=1',
46             visible => 'BOOL=false',
47             active => 'BOOL=true',
48             think_time => 'INT=0',
49             next_think => 'INT=0',
50             inputs => 'ARRAY=0',
51             outputs => 'ARRAY=0',
52             state_endtime => 'INT=',
53             state_target => 'INT=',
54             class => 'STR=',
55             info => 'STR=',
56             };
57 12   100     65 $self->{class} = $args->{class} || 'Games::3D::Thingy';
58 12         32 $self;
59             }
60              
61             sub class
62             {
63 0     0 1 0 my $self = shift;
64 0         0 $self->{class};
65             }
66              
67             sub id
68             {
69 1     1 1 696 my $self = shift;
70              
71 1         6 $self->{id};
72             }
73              
74             sub create_thing
75             {
76             # take your own blueprint and create a thing
77 3     3 1 5 my $self = shift;
78              
79 3   50     20 my $base = $self->{base} || 'Games::3D::Thingy';
80            
81 3 100       16 if (exists $self->{valid}->{base})
82             {
83 1         5 $base = $self->{valid}->{base};
84             }
85              
86 3         9 my $base_pm = $base; $base_pm =~ s/::/\//g; $base_pm .= '.pm';
  3         15  
  3         7  
87 3         1381 require $base_pm;
88 3         25 my $object = $base->new();
89              
90             # rebless, from 'Games::3D::Thingy' into 'Games::3D::Thingy::Physical...'
91 3         10 $object->{class} = $self->{class};
92              
93             # Foo::Bar::Baz inherits from Foo::Bar and Foo, so check all of them
94             # TODO: we might just store the inherited stuff as to not always have
95             # to check overriden settings
96 3         16 my @classes = split /::/, $object->{class};
97              
98 3         11 while (@classes > 0)
99             {
100 6         14 my $class = join('::', @classes);
101 6         29 my $tpl = $self->{_world}->find_template($class);
102 6 50       26 $tpl->init_thing($object) if $tpl;
103 6         19 pop @classes;
104             }
105 3         30 $object;
106             }
107              
108             sub init_thing
109             {
110             # init all fields in a thing from the blueprint and return the thing
111 7     7 1 12 my ($self,$thing) = @_;
112              
113 7         10 foreach my $key (keys %{$self->{valid}})
  7         42  
114             {
115 117 100       341 next if exists $thing->{$key};
116 16         53 my ($type,$default) = split /=/, $self->{valid}->{$key};
117 16 100       48 ($type,$default) = ('STR', $type) unless defined $default;
118              
119 16 50       77 if ($type eq 'ARRAY')
    50          
    50          
    50          
    100          
120             {
121 0         0 $thing->{$key} = [ split /,/, $default ];
122             }
123             elsif ($type eq 'BOOL')
124             {
125 0 0       0 $thing->{$key} = $default =~ /^(false|off|no)$/i ? undef : 1;
126             }
127             elsif ($type eq 'CODE')
128             {
129 0         0 $thing->{$key} = $default;
130             }
131             elsif ($type eq 'SIG')
132             {
133 0         0 $thing->{$key} = signal_by_name($default);
134             }
135             elsif ($type eq 'FRACT')
136             {
137 8         24 $thing->{$key} = abs($default);
138 8 50       27 $thing->{$key} = 1 if $thing->{$key} > 1;
139             }
140             else
141             {
142 8         28 $thing->{$key} = $default;
143             }
144             }
145 7         24 $thing;
146             }
147              
148             sub validate
149             {
150             # check whether a given key is allowed, and whether his data confirms to the
151             # blueprint
152 4     4 1 11 my $self = shift;
153 4         7 my $obj = shift;
154              
155 4         7 my $class = $self->{class};
156              
157             return
158 4 100       23 "Object class '". ref($obj)."' does not match template class '".$class."'"
159             unless $class eq ref($obj);
160              
161 3 50       28 return $self->validate_key($obj, $_[0]) if (@_ > 0);
162              
163 3         12 foreach my $key (keys %$obj)
164             {
165 39 100       65 next if $key =~ /^_/; # skip internals
166 36         56 my $rc = $self->validate_key($obj, $key);
167 36 100       70 return $rc if defined $rc; # error?
168             }
169 2         18 return; # okay
170             }
171              
172             sub validate_key
173             {
174 36     36 1 44 my ($self,$obj,$key) = @_;
175            
176 36 100 50     77 return "Invalid key '$key' on object " . ref($obj) . " #" . ($obj->{id}||-1)
177             unless exists $self->{valid}->{$key};
178              
179 35         43 return; # okay
180             }
181              
182             sub from_string
183             {
184 2     2 1 6 my ($str) = shift;
185              
186 2         31 my @lines = split /\n/, $str;
187            
188 2         8 my ($name,@objects,$line);
189 2         14 my $linenr = 0;
190 2         30 while (@lines > 0)
191             {
192 16         26 $line = shift @lines; $linenr++;
  16         22  
193 16 100       64 next if $line =~ /^\s*(#|$)/; # skip comments
194              
195 10 50       48 return ("Invalid format in string: '$line' in line $linenr")
196             unless ($line =~ /\s*([\w:]+)\s*\{/); # declaration: Class {
197              
198 10         25 my $class = $1;
199 10 50 50     42 return "Undefined class in line $linenr" if ($class || '') eq '';
200              
201 10         31 my $self = __PACKAGE__->new(); # emulate ->new();
202 10         25 $self->{class} = $class;
203              
204 10         17 $line = shift @lines; $linenr++;
  10         15  
205 10         18 my $s = $self->{valid};
206              
207 10         45 while ($line !~ /^\s*\}/)
208             {
209 37 50       470 if ( $line =~ m/\s*([\w-]+)\s*=>?\s*\{\s*$/) # "hash => {"
210             {
211 0   0     0 $name = $1 || return ("Empty hash name in line $linenr\n");
212 0         0 $s->{$name} = {};
213 0         0 $line = shift @lines; $linenr++;
  0         0  
214 0         0 while ($line !~ /^\s*\}/)
215             {
216             # var => val, var = val
217 0 0       0 return "Invalid line format in line $linenr\n" unless
218             $line =~
219             m/\s*([\w-]+)\s*=>?\s*(['\"])?(.*?)\2?\s*$/;
220 0   0     0 my $n = $1 || return ("Empty name in line $linenr\n");
221             # return ("Field '$n' already defined in hash '$name' in '$class' in line $linenr")
222             # if exists $s->{$name}->{$n};
223 0         0 $s->{$name}->{$n} = $3;
224 0         0 $line = shift @lines; $linenr++;
  0         0  
225             }
226             }
227             else
228             {
229 37 50       270 return "Invalid line format in line $linenr\n" unless
230             $line =~
231             m/\s*([\w-]+)\s*=>?\s*(['\"])?(.*?)\2?\s*$/; # var => val, var = val
232 37   50     113 $name = $1 || return ("Empty name in line $linenr\n");
233             # return ("Field '$name' already defined in '$class' in line $linenr")
234             # if exists $s->{$name};
235 37         112 $s->{$name} = $3;
236 37         52 $line = shift @lines; $linenr++;
  37         121  
237             }
238             }
239             # one object done
240 10         40 push @objects, $self;
241             }
242 2 100       18 wantarray ? @objects : $objects[0];
243             }
244              
245             sub as_string
246             {
247 4     4 1 707 my $self = shift;
248              
249 4         12 my $txt = $self->{class} . " {\n";
250 4         6 my $s = $self->{valid};
251 4         48 foreach my $k (sort keys %$s)
252             {
253 66 50       154 next if $k =~ /^_/; # skip internal keys
254 66         99 my $v = $s->{$k}; # get key
255 66 50       121 next if !defined $v; # skip empty
256 66 50       112 if (ref($v) eq 'HASH')
257             {
258 0         0 $v = "{\n";
259 0         0 foreach my $key (sort keys %{$s->{$k}})
  0         0  
260             {
261 0         0 my $vi = $s->{$k}->{$key};
262 0 0       0 $vi = $vi->as_string() if ref($v);
263 0         0 $v .= " $key = $vi\n";
264             }
265 0         0 $v .= " }";
266             }
267             else
268             {
269 66 50       138 $v = '"'.$v.'"' if $v =~ /[^a-zA-Z0-9_\.,'"+-=]/;
270 66 50       110 next if $v eq '';
271             }
272 66         139 $txt .= " $k = $v\n";
273             }
274 4         28 $txt .= "}\n";
275             }
276              
277             sub add_key
278             {
279 2     2 1 6 my ($self,$key,$data) = @_;
280              
281 2         10 $self->{valid}->{$key} = $data;
282 2         8 $self;
283             }
284              
285             sub keys
286             {
287 3     3 1 7 my ($self) = @_;
288              
289 3         4 scalar keys %{$self->{valid}};
  3         17  
290             }
291              
292             1;
293              
294             __END__