line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Fukurama::Class::Implements; |
2
|
5
|
|
|
5
|
|
29653
|
use Fukurama::Class::Version(0.02); |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
41
|
|
3
|
5
|
|
|
5
|
|
31
|
use Fukurama::Class::Rigid; |
|
5
|
|
|
|
|
556
|
|
|
5
|
|
|
|
|
38
|
|
4
|
5
|
|
|
5
|
|
31
|
use Fukurama::Class::Carp; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
42
|
|
5
|
5
|
|
|
5
|
|
654
|
use Fukurama::Class::Tree(); |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
2523
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $LEVEL_DISABLE = 0; |
8
|
|
|
|
|
|
|
our $LEVEL_CHECK_NONE = 1; |
9
|
|
|
|
|
|
|
our $LEVEL_CHECK_ALL = 2; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $CHECK_LEVEL = $LEVEL_CHECK_ALL; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
my $ERRORS = {}; |
14
|
|
|
|
|
|
|
my $ISA_ALREADY_DECORATED; |
15
|
|
|
|
|
|
|
my $REGISTER = {}; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 NAME |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Fukurama::Class::Implements - Pragma to provide interfaces |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 VERSION |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
Version 0.02 (beta) |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
package MyClass; |
28
|
|
|
|
|
|
|
use Fukurama::Class::Implements('MyParent'); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 DESCRIPTION |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
This pragma-like module enables te possibility to use interfaces (like in java). The implementation |
33
|
|
|
|
|
|
|
of all subroutines (except perls speacials) will be checked at compiletime. Your package won't inherit |
34
|
|
|
|
|
|
|
from this interface but every isa() will say that it is. Use Fukurama::Class instead, to get all the |
35
|
|
|
|
|
|
|
features for OO. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head1 CONFIG |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
You can define the check-level which describes how the module will check implementations. |
40
|
|
|
|
|
|
|
The following levels are allowed: |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=over 4 |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=item DISABLE (0) |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
There is no check and no change in UNIVERSAL. If you use this level, it's like you remove this module. |
47
|
|
|
|
|
|
|
There are no side effects. You should only use this, if you never use the isa() method to check for interfaces. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item CHECK_NONE (1) |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
All Registration-Processes are executed and UNIVERSAL::isa would be decorated, but there would be no check. |
52
|
|
|
|
|
|
|
This level is recommended for production. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item CHECK_ALL (2) |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
All Classes would checked for implementation. This is the default behavior when you does'n change the |
57
|
|
|
|
|
|
|
check-level. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=back |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 EXPORT |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=over 4 |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=item UNIVERSAL::isa |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
would be decorated |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=back |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 METHODS |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=over 4 |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item implements( child_class:STRING, interface_class:STRING ) return:VOID |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Helper-method, which would executed by every pragma usage. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=item run_check() return:VOID |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Helper method for static perl (see Fukurama::Class > BUGS) |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=item register_class_tree() return:VOID |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
Helper method to register needed handler in Fukurama::Class::Tree |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=back |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head1 AUTHOR, BUGS, SUPPORT, ACKNOWLEDGEMENTS, COPYRIGHT & LICENSE |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
see perldoc of L |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=cut |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# void |
96
|
|
|
|
|
|
|
my $BUILD_HANDLER = sub { |
97
|
|
|
|
|
|
|
my $classname = $_[0]; |
98
|
|
|
|
|
|
|
my $classdef = $_[1]; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $interface_def = $REGISTER->{$classname}; |
101
|
|
|
|
|
|
|
return if(!$interface_def); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
my $inheritation_paths = []; |
104
|
|
|
|
|
|
|
foreach my $interface (keys(%$interface_def)) { |
105
|
|
|
|
|
|
|
my $interface_inheritation_paths = Fukurama::Class::Tree->get_inheritation_path($interface); |
106
|
|
|
|
|
|
|
foreach my $path (@$interface_inheritation_paths) { |
107
|
|
|
|
|
|
|
unshift(@$path, $interface); |
108
|
|
|
|
|
|
|
push(@$inheritation_paths, $path); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
push(@$inheritation_paths, [$interface]) if(!scalar(@$interface_inheritation_paths)); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
$classdef->{'implements'} = $inheritation_paths if(scalar(@$inheritation_paths)); |
113
|
|
|
|
|
|
|
return; |
114
|
|
|
|
|
|
|
}; |
115
|
|
|
|
|
|
|
# void |
116
|
|
|
|
|
|
|
my $CHECK_HANDLER = sub { |
117
|
|
|
|
|
|
|
my $classname = $_[0]; |
118
|
|
|
|
|
|
|
my $classdef = $_[1]; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
return if($CHECK_LEVEL <= $LEVEL_CHECK_NONE); |
121
|
|
|
|
|
|
|
my $paths = $classdef->{'implements'}; |
122
|
|
|
|
|
|
|
return if(ref($paths) ne 'ARRAY'); |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
my $interface_list = {}; |
125
|
|
|
|
|
|
|
foreach my $path (@$paths) { |
126
|
|
|
|
|
|
|
my $level = 0; |
127
|
|
|
|
|
|
|
foreach my $class (@$path) { |
128
|
|
|
|
|
|
|
++$level; |
129
|
|
|
|
|
|
|
$interface_list->{$class} ||= ($level == 1 ? 1 : 0); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
__PACKAGE__->_check_implementations($classname, $interface_list); |
133
|
|
|
|
|
|
|
return; |
134
|
|
|
|
|
|
|
}; |
135
|
|
|
|
|
|
|
# AUTOMAGIC void |
136
|
|
|
|
|
|
|
sub import { |
137
|
9
|
|
|
9
|
|
2181
|
my $class = $_[0]; |
138
|
9
|
|
|
|
|
18
|
my $interface = $_[1]; |
139
|
9
|
|
|
|
|
15
|
my $import_depth = $_[2]; |
140
|
|
|
|
|
|
|
|
141
|
9
|
|
50
|
|
|
45
|
$import_depth ||= 0; |
142
|
9
|
|
|
|
|
68
|
my $child = [caller($import_depth)]->[0]; |
143
|
9
|
|
|
|
|
44
|
$class->implements($child, $interface); |
144
|
8
|
|
|
|
|
496
|
return undef; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
# void |
147
|
|
|
|
|
|
|
sub implements { |
148
|
13
|
|
|
13
|
1
|
21
|
my $class = $_[0]; |
149
|
13
|
|
|
|
|
21
|
my $child = $_[1]; |
150
|
13
|
|
|
|
|
20
|
my $interface = $_[2]; |
151
|
|
|
|
|
|
|
|
152
|
13
|
50
|
|
|
|
39
|
return if($CHECK_LEVEL == $LEVEL_DISABLE); |
153
|
|
|
|
|
|
|
|
154
|
5
|
|
|
5
|
|
34
|
no strict 'refs'; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
4844
|
|
155
|
|
|
|
|
|
|
|
156
|
13
|
|
|
|
|
43
|
$class->_decorate_isa(); |
157
|
13
|
50
|
66
|
1
|
|
16
|
if(!%{"$interface\::"} && !eval("use $interface();return 1")) { |
|
13
|
|
|
|
|
151
|
|
|
1
|
|
|
|
|
440
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
158
|
1
|
|
|
|
|
7
|
_croak($@); |
159
|
|
|
|
|
|
|
} |
160
|
12
|
|
100
|
|
|
60
|
$REGISTER->{$child} ||= {}; |
161
|
12
|
|
|
|
|
39
|
$REGISTER->{$child}->{$interface} = undef; |
162
|
12
|
|
|
|
|
43
|
$class->register_class_tree(); |
163
|
12
|
|
|
|
|
29
|
return; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
# void |
166
|
|
|
|
|
|
|
sub register_class_tree { |
167
|
15
|
|
|
15
|
1
|
27
|
my $class = $_[0]; |
168
|
|
|
|
|
|
|
|
169
|
15
|
|
|
|
|
78
|
Fukurama::Class::Tree->register_build_handler($BUILD_HANDLER); |
170
|
15
|
|
|
|
|
55
|
Fukurama::Class::Tree->register_check_handler($CHECK_HANDLER); |
171
|
15
|
|
|
|
|
26
|
return; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
# void |
174
|
|
|
|
|
|
|
sub run_check { |
175
|
15
|
|
|
15
|
1
|
7162
|
my $class = $_[0]; |
176
|
15
|
|
|
|
|
30
|
my $type = $_[1]; |
177
|
|
|
|
|
|
|
|
178
|
15
|
50
|
|
|
|
64
|
return if($CHECK_LEVEL <= $LEVEL_CHECK_NONE); |
179
|
15
|
100
|
|
|
|
51
|
$type = 'MANUAL' if(!defined($type)); |
180
|
|
|
|
|
|
|
|
181
|
15
|
50
|
|
|
|
59
|
if($CHECK_LEVEL == $LEVEL_CHECK_ALL) { |
182
|
15
|
|
|
|
|
89
|
Fukurama::Class::Tree->run_check($type); |
183
|
|
|
|
|
|
|
} |
184
|
11
|
|
|
|
|
127
|
return; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
# void |
187
|
|
|
|
|
|
|
sub _check_implementations { |
188
|
21
|
|
|
21
|
|
37
|
my $class = $_[0]; |
189
|
21
|
|
|
|
|
36
|
my $checked_class = $_[1]; |
190
|
21
|
|
|
|
|
35
|
my $checked_class_interfaces = $_[2]; |
191
|
|
|
|
|
|
|
|
192
|
21
|
|
|
|
|
37
|
my $error_list = []; |
193
|
21
|
|
|
|
|
40
|
my $interface_defs = []; |
194
|
21
|
|
|
|
|
361
|
my @interfaces = keys(%$checked_class_interfaces); |
195
|
21
|
|
|
|
|
46
|
foreach my $interface (@interfaces) { |
196
|
47
|
|
|
|
|
250
|
push(@$interface_defs, { |
197
|
|
|
|
|
|
|
class => $interface, |
198
|
|
|
|
|
|
|
subs => [Fukurama::Class::Tree->get_class_subs($interface)], |
199
|
|
|
|
|
|
|
}); |
200
|
|
|
|
|
|
|
} |
201
|
21
|
|
|
|
|
47
|
my $class_def = {}; |
202
|
21
|
|
|
|
|
83
|
foreach my $sub (Fukurama::Class::Tree->get_class_subs($checked_class)) { |
203
|
32
|
|
|
|
|
89
|
$class_def->{$sub} = undef; |
204
|
|
|
|
|
|
|
} |
205
|
21
|
|
|
|
|
93
|
$class->_check_class_def($checked_class, $class_def, $interface_defs, $error_list); |
206
|
|
|
|
|
|
|
|
207
|
21
|
100
|
|
|
|
54
|
if(@$error_list) { |
208
|
5
|
|
|
|
|
10
|
my $errors = ''; |
209
|
5
|
|
|
|
|
12
|
foreach my $e (@$error_list) { |
210
|
11
|
|
|
|
|
29
|
my $key = $e->{'class'} . '-' . $e->{'method'}; |
211
|
11
|
100
|
|
|
|
33
|
next if($ERRORS->{$key}); |
212
|
4
|
|
|
|
|
17
|
$errors .= "\n > You doesn't implement method '$e->{method}' in class '$e->{class}' which is defined in interface(es): " . |
213
|
4
|
|
|
|
|
15
|
join(', ', @{$e->{interfaces}}); |
214
|
4
|
|
|
|
|
17
|
$ERRORS->{$key} = 1; |
215
|
|
|
|
|
|
|
} |
216
|
5
|
100
|
|
|
|
34
|
_croak(scalar(@$error_list) . " Interface-Error(s):$errors\n", 1) if($errors); |
217
|
|
|
|
|
|
|
} |
218
|
19
|
|
|
|
|
93
|
return; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
# void |
221
|
|
|
|
|
|
|
sub _check_class_def { |
222
|
21
|
|
|
21
|
|
40
|
my $class = $_[0]; |
223
|
21
|
|
|
|
|
41
|
my $obj_class = $_[1]; |
224
|
21
|
|
|
|
|
37
|
my $class_def = $_[2]; |
225
|
21
|
|
|
|
|
30
|
my $interface_defs = $_[3]; |
226
|
21
|
|
|
|
|
32
|
my $errorlist = $_[4]; |
227
|
|
|
|
|
|
|
|
228
|
21
|
|
|
|
|
69
|
my $interface_methods = $class->_merge_interface_methods($interface_defs); |
229
|
21
|
|
|
|
|
97
|
foreach my $method (keys %$interface_methods) { |
230
|
36
|
|
|
|
|
126
|
$class->_check_method_implementation($obj_class, $method, exists($class_def->{$method}), $interface_methods->{$method}, $errorlist); |
231
|
|
|
|
|
|
|
} |
232
|
21
|
|
|
|
|
71
|
return; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
# void |
235
|
|
|
|
|
|
|
sub _check_method_implementation { |
236
|
36
|
|
|
36
|
|
69
|
my $class = $_[0]; |
237
|
36
|
|
|
|
|
49
|
my $obj_class = $_[1]; |
238
|
36
|
|
|
|
|
42
|
my $method = $_[2]; |
239
|
36
|
|
|
|
|
56
|
my $class_method_exist = $_[3]; |
240
|
36
|
|
|
|
|
48
|
my $interface_method_list = $_[4]; |
241
|
36
|
|
|
|
|
46
|
my $error_list = $_[5]; |
242
|
|
|
|
|
|
|
|
243
|
36
|
100
|
|
|
|
83
|
if(!$class_method_exist) { |
244
|
11
|
|
|
|
|
20
|
my $definitions = []; |
245
|
11
|
|
|
|
|
20
|
foreach my $interface (@$interface_method_list) { |
246
|
17
|
|
|
|
|
42
|
push(@$definitions, $interface); |
247
|
|
|
|
|
|
|
} |
248
|
11
|
|
|
|
|
140
|
push(@$error_list, { |
249
|
|
|
|
|
|
|
class => $obj_class, |
250
|
|
|
|
|
|
|
method => $method, |
251
|
|
|
|
|
|
|
interfaces => $definitions, |
252
|
|
|
|
|
|
|
}); |
253
|
|
|
|
|
|
|
} |
254
|
36
|
|
|
|
|
101
|
return; |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
# hash[] |
257
|
|
|
|
|
|
|
sub _merge_interface_methods { |
258
|
21
|
|
|
21
|
|
33
|
my $class = $_[0]; |
259
|
21
|
|
|
|
|
30
|
my $interface_defs = $_[1]; |
260
|
|
|
|
|
|
|
|
261
|
21
|
|
|
|
|
37
|
my $methodnames = {}; |
262
|
21
|
|
|
|
|
151
|
foreach my $def (@$interface_defs) { |
263
|
47
|
|
|
|
|
68
|
foreach my $method (@{$def->{'subs'}}) { |
|
47
|
|
|
|
|
104
|
|
264
|
59
|
|
100
|
|
|
314
|
$methodnames->{$method} ||= []; |
265
|
59
|
|
|
|
|
68
|
push(@{$methodnames->{$method}}, $def->{'class'}); |
|
59
|
|
|
|
|
203
|
|
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
} |
268
|
21
|
|
|
|
|
55
|
return $methodnames; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
# string{} |
271
|
|
|
|
|
|
|
sub _has_interface { |
272
|
41539
|
|
|
41539
|
|
49125
|
my $class = $_[0]; |
273
|
41539
|
|
|
|
|
50002
|
my $obj_class = $_[1]; |
274
|
41539
|
|
|
|
|
44859
|
my $interface_class = $_[2]; |
275
|
|
|
|
|
|
|
|
276
|
41539
|
50
|
|
|
|
93709
|
return 0 if(!defined($obj_class)); |
277
|
41539
|
|
|
|
|
58079
|
my $interfaces = $REGISTER->{$obj_class}; |
278
|
41539
|
100
|
66
|
|
|
156659
|
return 0 if(!$interfaces || !exists($interfaces->{$interface_class})); |
279
|
9
|
|
|
|
|
59
|
return 1; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
# void |
282
|
|
|
|
|
|
|
sub _decorate_isa { |
283
|
13
|
|
|
13
|
|
21
|
my $class = $_[0]; |
284
|
|
|
|
|
|
|
|
285
|
5
|
|
|
5
|
|
37
|
no strict 'refs'; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
163
|
|
286
|
5
|
|
|
5
|
|
27
|
no warnings 'redefine'; |
|
5
|
|
|
|
|
14
|
|
|
5
|
|
|
|
|
847
|
|
287
|
|
|
|
|
|
|
|
288
|
13
|
100
|
|
|
|
52
|
return if($ISA_ALREADY_DECORATED); |
289
|
|
|
|
|
|
|
|
290
|
3
|
|
|
|
|
7
|
my $identifier = 'UNIVERSAL::isa'; |
291
|
3
|
|
|
|
|
6
|
my $old = *{$identifier}{'CODE'}; |
|
3
|
|
|
|
|
11
|
|
292
|
3
|
50
|
|
|
|
17
|
die("Unable to decorate non existing sub $identifier") if(!$old); |
293
|
|
|
|
|
|
|
|
294
|
3
|
|
|
|
|
12
|
*{$identifier} = sub { |
295
|
41539
|
|
|
41539
|
|
374255
|
my $obj_class = $_[0]; |
296
|
41539
|
|
|
|
|
47100
|
my $type = $_[1]; |
297
|
|
|
|
|
|
|
|
298
|
41539
|
100
|
|
|
|
91190
|
return 1 if($class->_has_interface($obj_class, $type)); |
299
|
|
|
|
|
|
|
|
300
|
41530
|
|
|
|
|
333036
|
goto &$old; |
301
|
3
|
|
|
|
|
17
|
}; |
302
|
3
|
|
|
|
|
8
|
$ISA_ALREADY_DECORATED = 1; |
303
|
3
|
|
|
|
|
7
|
return; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
5
|
|
|
5
|
|
28
|
no warnings 'void'; # avoid 'Too late to run CHECK/INIT block' |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
471
|
|
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# AUTOMAGIC void |
309
|
|
|
|
|
|
|
sub CHECK { |
310
|
5
|
|
|
5
|
|
29
|
__PACKAGE__->run_check('CHECK'); |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
# AUTOMAGIC void |
313
|
|
|
|
|
|
|
sub END { |
314
|
5
|
|
|
5
|
|
2121
|
__PACKAGE__->run_check('END'); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
1; |