File Coverage

blib/lib/IOC/Config/XML/SAX/Handler.pm
Criterion Covered Total %
statement 156 156 100.0
branch 75 82 91.4
condition 21 32 65.6
subroutine 35 35 100.0
pod 4 4 100.0
total 291 309 94.1


line stmt bran cond sub pod time code
1              
2             package IOC::Config::XML::SAX::Handler;
3              
4 4     4   21 use strict;
  4         8  
  4         161  
5 4     4   19 use warnings;
  4         8  
  4         342  
6              
7             our $VERSION = '0.02';
8              
9 4     4   19 use IOC::Exceptions;
  4         9  
  4         87  
10              
11 4     4   2599 use IOC::Registry;
  4         14  
  4         908  
12 4     4   17332 use IOC::Container;
  4         16  
  4         148  
13 4     4   4567 use IOC::Service;
  4         14  
  4         166  
14 4     4   2436 use IOC::Service::Literal;
  4         14  
  4         102  
15 4     4   2354 use IOC::Service::ConstructorInjection;
  4         8  
  4         90  
16 4     4   2124 use IOC::Service::SetterInjection;
  4         12  
  4         102  
17 4     4   2107 use IOC::Service::Prototype;
  4         12  
  4         95  
18 4     4   3885 use IOC::Service::Prototype::ConstructorInjection;
  4         11  
  4         115  
19 4     4   2574 use IOC::Service::Prototype::SetterInjection;
  4         10  
  4         112  
20 4     4   2246 use IOC::Service::Parameterized;
  4         11  
  4         132  
21              
22 4     4   30 use base qw(XML::SAX::Base);
  4         7  
  4         28517  
23              
24             sub new {
25 28     28 1 62 my $class = shift;
26 28         637 my $self = $class->SUPER::new(@_);
27 28         8647 $self->{registry} = undef;
28 28         118 $self->{current} = undef;
29 28         56 $self->{current_service} = undef;
30 28         100 return $self;
31             }
32              
33             ## XML::SAX Handlers
34              
35             sub start_element {
36 122     122 1 138348 my ($self, $el) = @_;
37 122         367 my $type = lc($el->{Name});
38 122 100       7132 if ($type eq 'registry') {
    100          
39 27         446 $self->_createRegistry($el);
40             }
41             elsif (defined($self->{registry})) {
42 94 100       382 if ($type eq 'container') {
    100          
    100          
    100          
    100          
43 26         33438 $self->_createContainer($el);
44             }
45             elsif ($type eq 'service') {
46 35         134 $self->_createService($el);
47             }
48             elsif ($type eq 'class') {
49 16         71 $self->_createClass($el);
50             }
51             elsif ($type eq 'parameter') {
52 11         44 $self->_createConstructorParameter($el);
53             }
54             elsif ($type eq 'setter') {
55 4         31 $self->_createSetterParameter($el);
56             }
57             }
58             else {
59 1         364 throw IOC::ConfigurationError "$type is not allowed unless a Registry is created first";
60             }
61             }
62              
63             sub end_element {
64 83     83 1 10270 my ($self, $el) = @_;
65 83         226 my $name = lc($el->{Name});
66 83 100       448 if ($name eq 'container') {
    100          
67 9         34 $self->_finishContainer();
68             }
69             elsif ($name eq 'service') {
70 32         107 $self->_finishService();
71             }
72              
73             }
74              
75             sub characters {
76 157     157 1 14082 my ($self, $el) = @_;
77 157         275 my $data = $el->{Data};
78 157 100       971 return if $data =~ /^\s+$/;
79 25 100       123 $self->_handleServiceCharacterData($data) if $self->{current_service};
80             }
81              
82             ## basic utility routines
83              
84             sub _getName {
85 77     77   139 my ($self, $el) = @_;
86 77         15731 return $el->{Attributes}->{'{}name'}->{Value};
87             }
88              
89             sub _getValue {
90 151     151   285 my ($self, $el, $key) = @_;
91 151 100       736 return undef unless exists $el->{Attributes}->{'{}' . $key};
92 111         650 return $el->{Attributes}->{'{}' . $key}->{Value};
93             }
94              
95             sub _compilePerl {
96 8     8   32 my ($self, $perl) = @_;
97 8         748 my $value = eval $perl;
98 8 100       154 throw IOC::OperationFailed "Could not compile '$perl'", $@ if $@;
99 6         61 return $value;
100             }
101              
102             ## IOC::Registry handler
103              
104             sub _createRegistry {
105 27     27   70 my ($self, $el) = @_;
106 27 50       124 (!defined($self->{registry})) ||
107             throw IOC::ConfigurationError "We already have a registry";
108 27         210 $self->{registry} = IOC::Registry->new();
109 27         360 $self->{current} = $self->{registry};
110             }
111              
112             ## IOC::Container handler(s)
113              
114             sub _createContainer {
115 26     26   66 my ($self, $el) = @_;
116 26 100       116 ($self->_getValue($el, 'name'))
117             || throw IOC::ConfigurationError "Container must have name";
118 25         109 my $c = IOC::Container->new($self->_getName($el));
119 25 100       257 if ($self->{current}->isa('IOC::Registry')) {
    50          
120 24         113 $self->{current}->registerContainer($c);
121             }
122             elsif ($self->{current}->isa('IOC::Container')) {
123 1         7 $self->{current}->addSubContainer($c);
124             }
125 25         123 $self->{current} = $c;
126             }
127              
128             sub _finishContainer {
129 9     9   15 my ($self) = @_;
130 9 50       37 ($self->{current})
131             || throw IOC::ConfigurationError "This should never happen";
132 9 100 66     119 $self->{current} = $self->{current}->getParentContainer()
133             if $self->{current}->isa('IOC::Container') &&
134             !$self->{current}->isRootContainer();
135             }
136              
137             ## IOC::Service::* handler(s)
138              
139             sub _createService {
140 35     35   65 my ($self, $el) = @_;
141 35 100       323 (!$self->{current}->isa('IOC::Registry')) ||
142             throw IOC::ConfigurationError "Services must be within containers";
143 34 100       189 ($self->_getValue($el, 'name'))
144             || throw IOC::ConfigurationError "Service must have name";
145 32         161 $self->{current_service} = {
146             name => $self->_getName($el),
147             type => $self->_getValue($el, 'type'),
148             prototype => $self->_getValue($el, 'prototype'),
149             };
150             }
151              
152             sub _createClass {
153 16     16   32 my ($self, $el) = @_;
154 16 50       65 ($self->{current_service}) ||
155             throw IOC::ConfigurationError "Class must be within Services";
156 16         51 $self->{current_service}->{class} = {
157             name => $self->_getName($el),
158             constructor => $self->_getValue($el, 'constructor')
159             };
160             }
161              
162             sub _createConstructorParameter {
163 11     11   21 my ($self, $el) = @_;
164 11 50 33     111 ($self->{current_service} &&
      33        
165             ($self->{current_service}->{type} eq 'ConstructorInjection' &&
166             exists $self->{current_service}->{class})) ||
167             throw IOC::ConfigurationError "Paramter must be after Class and must be within Services";
168 11 100       86 unless (exists $self->{current_service}->{parameters}) {
169 7         27 $self->{current_service}->{parameters} = [];
170             }
171 11         19 push @{$self->{current_service}->{parameters}} => {
  11         37  
172             type => $self->_getValue($el, 'type')
173             };
174             }
175              
176             sub _createSetterParameter {
177 4     4   8 my ($self, $el) = @_;
178 4 50 33     48 ($self->{current_service} &&
      33        
179             ($self->{current_service}->{type} eq 'SetterInjection' &&
180             exists $self->{current_service}->{class})) ||
181             throw IOC::ConfigurationError "Paramter must be after Class and must be within Services";
182 4 100       16 unless (exists $self->{current_service}->{setters}) {
183 3         10 $self->{current_service}->{setters} = [];
184             }
185 4         8 push @{$self->{current_service}->{setters}} => {
  4         15  
186             name => $self->_getName($el)
187             };
188             }
189              
190             sub _handleServiceCharacterData {
191 24     24   44 my ($self, $data) = @_;
192 24 100       76 if ($self->{current_service}->{parameters}) {
193 9         29 $self->{current_service}->{parameters}->[-1]->{data} = $data;
194             }
195 24 100       71 if ($self->{current_service}->{setters}) {
196 4         22 $self->{current_service}->{setters}->[-1]->{data} = $data;
197             }
198             else {
199 20         112 $self->{current_service}->{data} = $data;
200             }
201             }
202              
203             sub _finishService {
204 32     32   61 my ($self) = @_;
205 32         68 my $service_desc = $self->{current_service};
206 32         83 $service_desc->{service_class} = 'IOC::Service';
207 32 100 100     154 $service_desc->{service_class} .= '::Prototype'
208             if $service_desc->{prototype} && lc($service_desc->{prototype}) ne 'false';
209             # NOTE:
210             # this allows for us to add on more Service
211             # types without too much trouble ...
212 32   100     281 my $constructor = $self->can('__makeService' . ($service_desc->{type} || ''));
213 32 100       95 if ($constructor) {
214 31         92 $self->$constructor($service_desc);
215             }
216             else {
217 1         14 throw IOC::ConfigurationError "Unrecognized type : " . $service_desc->{type};
218             }
219 18         123 $self->{current_service} = undef;
220             }
221              
222             ## ultra-private Service constructors
223              
224             sub __makeService {
225 5     5   12 my ($self, $service_desc) = @_;
226             # we have a plain Service
227 5 100       40 ($service_desc->{data})
228             || throw IOC::ConfigurationError "No sub in Service";
229 4         30 $self->{current}->register(
230             $service_desc->{service_class}->new(
231             $service_desc->{name} => $self->_compilePerl('sub { ' . $service_desc->{data} . ' }')
232             )
233             );
234             }
235              
236             sub __makeServiceParameterized {
237 1     1   2 my ($self, $service_desc) = @_;
238             # we have a plain Service
239 1 50       5 ($service_desc->{data})
240             || throw IOC::ConfigurationError "No sub in Service";
241 1         16 $self->{current}->register(
242             IOC::Service::Parameterized->new(
243             $service_desc->{name} => $self->_compilePerl('sub { ' . $service_desc->{data} . ' }')
244             )
245             );
246             }
247              
248             sub __makeServiceLiteral {
249 7     7   13 my ($self, $service_desc) = @_;
250 7 100       52 (exists $service_desc->{data})
251             || throw IOC::ConfigurationError "Cant make a Literal without a value";
252 5         45 $self->{current}->register(
253             IOC::Service::Literal->new($service_desc->{name} => $service_desc->{data})
254             );
255             }
256              
257             sub __makeServiceConstructorInjection {
258 11     11   25 my ($self, $service_desc) = @_;
259 11 100 100     181 (exists $service_desc->{class} &&
      66        
260             ($service_desc->{class}->{name} && $service_desc->{class}->{constructor}))
261             || throw IOC::ConfigurationError "Cant make a ConstructorInjection without a class";
262 8         16 my @parameters;
263             @parameters = map {
264 11 100       34 if ($_->{type}) {
  7         22  
265 7 100       43 if ($_->{type} eq 'component') {
    100          
266 3         22 IOC::Service::ConstructorInjection->ComponentParameter($_->{data})
267             }
268             elsif ($_->{type} eq 'perl') {
269 3         12 $self->_compilePerl($_->{data})
270             }
271             else {
272 1         16 throw IOC::ConfigurationError "Unknown Type: " . $_->{type}
273             }
274             }
275             else {
276 4 100       29 (defined $_->{data})
277             || throw IOC::ConfigurationError "No data";
278 3         13 $_->{data}
279             }
280 8 100       40 } @{$service_desc->{parameters}}
281             if exists $service_desc->{parameters};
282 5         18 $service_desc->{service_class} .= '::ConstructorInjection';
283 5         67 $self->{current}->register(
284             $service_desc->{service_class}->new($service_desc->{name} => (
285             $service_desc->{class}->{name},
286             $service_desc->{class}->{constructor},
287             \@parameters
288             ))
289             );
290             }
291              
292             sub __makeServiceSetterInjection {
293 7     7   14 my ($self, $service_desc) = @_;
294 7 100 100     101 (exists $service_desc->{class} &&
      66        
295             ($service_desc->{class}->{name} && $service_desc->{class}->{constructor}))
296             || throw IOC::ConfigurationError "Cant make a ConstructorInjection without a class";
297 4         9 my @setters;
298 4         24 @setters = map {
299 3         9 { $_->{name} => $_->{data} }
300 4 100       17 } @{$service_desc->{setters}}
301             if exists $service_desc->{setters};
302 4         14 $service_desc->{service_class} .= '::SetterInjection';
303 4         67 $self->{current}->register(
304             $service_desc->{service_class}->new($service_desc->{name} => (
305             $service_desc->{class}->{name},
306             $service_desc->{class}->{constructor},
307             \@setters
308             ))
309             );
310             }
311              
312             1;
313              
314             __END__