File Coverage

blib/lib/PRANG/Graph/Choice.pm
Criterion Covered Total %
statement 96 101 95.0
branch 44 52 84.6
condition 20 25 80.0
subroutine 11 11 100.0
pod 0 5 0.0
total 171 194 88.1


line stmt bran cond sub pod time code
1              
2             package PRANG::Graph::Choice;
3             $PRANG::Graph::Choice::VERSION = '0.21';
4 11     11   2138 use 5.010;
  11         47  
5 11     11   71 use Moose;
  11         36  
  11         82  
6 11     11   71295 use MooseX::Params::Validate;
  11         28  
  11         94  
7 11     11   5397 use Moose::Util::TypeConstraints;
  11         26  
  11         118  
8 11     11   24567 use Moose::Meta::TypeConstraint;
  11         23  
  11         391  
9              
10             BEGIN {
11 11     11   67 class_type('Moose::Meta::TypeConstraint');
12             }
13              
14             has 'choices' =>
15             is => "ro",
16             isa => "ArrayRef[PRANG::Graph::Node]",
17             default => sub { [] },
18             ;
19              
20             has 'attrName' =>
21             is => "ro",
22             isa => "Str",
23             required => 1,
24             ;
25              
26             has 'type_map' =>
27             is => "ro",
28             isa => "HashRef[Str|Moose::Meta::TypeConstraint]",
29             predicate => "has_type_map",
30             ;
31              
32             has 'type_map_prefix' =>
33             is => "ro",
34             isa => "HashRef[Str]",
35             predicate => "has_type_map_prefix",
36             ;
37              
38             has 'xml_nodeName' =>
39             is => "ro",
40             isa => "Str",
41             predicate => "has_xml_nodeName",
42             ;
43              
44             has 'name_attr' =>
45             is => "ro",
46             isa => "Str",
47             predicate => "has_name_attr",
48             ;
49              
50             has 'xmlns_attr' =>
51             is => "ro",
52             isa => "Str",
53             predicate => "has_xmlns_attr",
54             ;
55              
56             has 'xmlns' =>
57             is => "ro",
58             isa => "Str",
59             predicate => "has_xmlns",
60             ;
61            
62             sub node_ok {
63 163     163 0 345 my $self = shift;
64 163         716 my ( $node, $ctx ) = pos_validated_list(
65             \@_,
66             { isa => 'XML::LibXML::Node' },
67             { isa => 'PRANG::Graph::Context' },
68             );
69            
70 163         34895 for my $choice ( @{ $self->choices } ) {
  163         4189  
71 312 100       1165 if ( defined $choice->node_ok($node, $ctx) ) {
72 117         408 return 1;
73             }
74             }
75 46         158 return;
76             }
77              
78             sub accept {
79 132     132 0 252 my $self = shift;
80 132         798 my ( $node, $ctx, $lax ) = pos_validated_list(
81             \@_,
82             { isa => 'XML::LibXML::Node' },
83             { isa => 'PRANG::Graph::Context' },
84             { isa => 'Bool', optional => 1 },
85             );
86              
87 132 100       42256 if ($ctx->chosen) {
88              
89             # this is a safe exception; the only time this graph
90             # node will be called repeatedly is if it is the root
91             # node for an element, due to the structure of
92             # PRANG::Graph::Context
93 2         23 $ctx->exception(
94             "Single child node expected, multiple found",
95             $node,
96             );
97             }
98              
99 130         230 my $num;
100 130 100       899 my $name = $node->isa("XML::LibXML::Text")
101             ? ""
102             : $node->localname;
103 130   100     704 my $xmlns = length($name) && $node->namespaceURI;
104 130         241 my ($key, $val, $x, $ns);
105 130         218 for my $choice ( @{ $self->choices } ) {
  130         3287  
106 221         376 $num++;
107 221 100       599 if ( defined $choice->node_ok($node, $ctx) ) {
108 130         505 ($key, $val, $x, $ns) = $choice->accept($node, $ctx, $lax);
109             }
110 215 100       655 if ($key) {
111 124         3165 $ctx->chosen($num);
112             return (
113 124   100     535 $key, $val, $x||eval{$choice->nodeName}||"",
114             $ns
115             );
116             }
117             }
118 0         0 return ();
119             }
120              
121             sub complete {
122 13     13 0 44 my $self = shift;
123 13         85 my ( $ctx ) = pos_validated_list(
124             \@_,
125             { isa => 'PRANG::Graph::Context' },
126             );
127            
128 13         2948 $ctx->chosen;
129             }
130              
131             sub expected {
132 3     3 0 10 my $self = shift;
133 3         17 my ( $ctx ) = pos_validated_list(
134             \@_,
135             { isa => 'PRANG::Graph::Context' },
136             );
137            
138 3 50       1073 if ( my $num = $ctx->chosen ) {
139 0         0 return $self->choices->[$num-1]->expected($ctx);
140             }
141             else {
142 3         10 my @choices;
143 3         7 for my $choice ( @{$self->choices} ) {
  3         82  
144 6         30 push @choices, $choice->expected($ctx);
145             }
146 3         16 return @choices;
147             }
148             }
149              
150             our $REGISTRY =
151             Moose::Util::TypeConstraints::get_type_constraint_registry();
152              
153             sub output {
154 67     67 0 207 my $self = shift;
155            
156             # First 3 args positional, rest are named
157             # Because we're making 2 validation calls, we have to use different cache keys
158 67         459 my ( $item, $node, $ctx ) = pos_validated_list(
159             [@_[0..2]],
160             { isa => 'Object' },
161             { isa => 'XML::LibXML::Element' },
162             { isa => 'PRANG::Graph::Context' },
163             MX_PARAMS_VALIDATE_CACHE_KEY => 'choice-output-positional',
164             );
165            
166 67         20776 my ( $value, $slot ) = validated_list(
167             [@_[3..$#_]],
168             value => { isa => 'Item', optional => 1 },
169             slot => { isa => 'Int', optional => 1 },
170             MX_PARAMS_VALIDATE_CACHE_KEY => 'choice-output-named',
171             );
172              
173 67         16070 my $an = $self->attrName;
174 67   66     238 $value //= $item->$an;
175 67         232 my ($name, $xmlns);
176 67 100 100     2044 if ( $self->has_name_attr || $self->has_xmlns_attr ) {
    50          
177 21 100       611 if ( $self->has_name_attr ) {
178 19         493 my $x = $self->name_attr;
179 19         524 $name = $item->$x;
180 19 100       70 if ( defined $slot ) {
181 17         41 $name = $name->[$slot];
182             }
183             }
184             else {
185 2   33     58 $name = $self->xml_nodeName // $an;
186             }
187 21 100       671 if ( $self->has_xmlns_attr ) {
188 19         507 my $attr_getter = $self->xmlns_attr;
189 19         518 $xmlns = $item->$attr_getter;
190 19 100       81 if ( defined $slot ) {
191 17         41 $xmlns = $xmlns->[$slot];
192             }
193             }
194             else {
195 2   50     51 $xmlns = $self->xmlns // "";
196             }
197             }
198             elsif ( $self->has_type_map ) {
199 46         1095 my $map = $self->type_map;
200 46         83 my $nodeCount = 0;
201 46         176 for my $element ( keys %$map ) {
202 128         252 my $type = $map->{$element};
203 128 100       277 if ( !ref $type ) {
204 17         87 $type = $map->{$element} =
205             $REGISTRY->get_type_constraint($type);
206             }
207            
208 128 100       1075 if ( $type->check($value) ) {
209 46         3251 $name = $element;
210 46         76 $nodeCount++;
211             }
212            
213 128 50       5849 if ($nodeCount > 1) {
214 0         0 $ctx->exception("More than 1 node type matched for value: '$value' " .
215             "You need a stricter type definition for your xml_nodeName map, or PRANG can't figure out which element name to emit");
216             }
217             }
218             }
219            
220 67 50       196 if ( !defined $name ) {
221 0         0 $ctx->exception(
222             "don't know what to serialize $value to for slot "
223             .$self->attrName
224             );
225             }
226 67 100       230 if ( length $name ) {
227 53 100 66     1684 if ( $self->has_type_map_prefix and $name =~ /(.*):(.*)/) {
228 2         6 $name = $2;
229 2         54 $xmlns = $self->type_map_prefix->{$1};
230             }
231 53         114 my $found;
232 53         76 for my $choice ( @{ $self->choices } ) {
  53         1425  
233 77 100       187 if ($xmlns) {
234 13 50       418 next unless $choice->has_xmlns;
235 13 100 100     316 next unless $choice->xmlns eq $xmlns or
236             $choice->xmlns eq "*";
237             }
238 75 100 100     1870 next unless $choice->nodeName eq $name or
239             $choice->nodeName eq "*";
240 53         146 $found++;
241 53 100       388 $choice->output(
    100          
242             $item,$node,$ctx,
243             value => $value,
244             (defined $slot ? (slot => $slot) : ()),
245             name => $name,
246             (defined $xmlns ? (xmlns => $xmlns) : ()),
247             );
248 53         7752 last;
249             }
250 53 50       465 if ( !$found ) {
251 0 0       0 $ctx->exception(
252             "don't know what to serialize $value to for slot "
253             .$self->attrName
254             ." (looked for '$name' node"
255             .(
256             $xmlns?" xmlns='$xmlns'":""
257             )
258             .")",
259             );
260             }
261             }
262             else {
263              
264             # textnode ... jfdi
265 14         120 my $tn = $self->createTextNode($node->ownerDocument, $value);
266 14         180 $node->appendChild($tn);
267             }
268             }
269              
270             with 'PRANG::Graph::Node';
271              
272             1;
273              
274             __END__
275              
276             =head1 NAME
277              
278             PRANG::Graph::Choice - accept multiple discrete node types
279              
280             =head1 SYNOPSIS
281              
282             See L<PRANG::Graph::Meta::Element> source and
283             L<PRANG::Graph::Node> for examples and information.
284              
285             =head1 DESCRIPTION
286              
287             This graph node specifies that the XML graph at this point may be one
288             of a list of text nodes or elements, depending on the type of entries
289             in the B<choices> property.
290              
291             If there is only one type of node allowed then the element does not
292             have one of these objects in their graph.
293              
294             =head1 ATTRIBUTES
295              
296             =over
297              
298             =item B<ArrayRef[PRANG::Graph::Node] choices>
299              
300             This property provides the next portion of the XML Graph. Depending
301             on the type of entry, it will accept and emit nodes of a particular
302             type.
303              
304             Entries must be one of L<PRANG::Graph::Element>, or
305             L<PRANG::Graph::Text>.
306              
307             =item B<HashRef[Str|Moose::Meta::TypeConstraint] type_map>
308              
309             This map is used for emitting. It maps from the localname of the XML
310             node to the type which that localname is appropriate for. This map
311             also needs to include the XML namespace, that it doesn't is currently a bug.
312              
313             =item B<Str name_attr>
314              
315             Used when emitting; avoid type-based selection and instead retrieve
316             the name of the XML node from this attribute.
317              
318             =item B<attrName>
319              
320             Used when emitting; specifies the method to call to retrieve the item
321             to be output.
322              
323             =back
324              
325             =head1 SEE ALSO
326              
327             L<PRANG::Graph::Meta::Class>, L<PRANG::Graph::Meta::Element>,
328             L<PRANG::Graph::Context>, L<PRANG::Graph::Node>
329              
330             Lower order L<PRANG::Graph::Node> types:
331              
332             L<PRANG::Graph::Element>, L<PRANG::Graph::Text>
333              
334             =head1 AUTHOR AND LICENCE
335              
336             Development commissioned by NZ Registry Services, and carried out by
337             Catalyst IT - L<http://www.catalyst.net.nz/>
338              
339             Copyright 2009, 2010, NZ Registry Services. This module is licensed
340             under the Artistic License v2.0, which permits relicensing under other
341             Free Software licenses.
342              
343             =cut
344