File Coverage

blib/lib/PRANG/Graph/Choice.pm
Criterion Covered Total %
statement 15 101 14.8
branch 0 52 0.0
condition 0 25 0.0
subroutine 6 11 54.5
pod 0 5 0.0
total 21 194 10.8


line stmt bran cond sub pod time code
1              
2             package PRANG::Graph::Choice;
3             $PRANG::Graph::Choice::VERSION = '0.19';
4 1     1   2183 use 5.010;
  1         3  
5 1     1   7 use Moose;
  1         2  
  1         9  
6 1     1   6423 use MooseX::Params::Validate;
  1         3  
  1         10  
7 1     1   425 use Moose::Util::TypeConstraints;
  1         2  
  1         10  
8 1     1   2374 use Moose::Meta::TypeConstraint;
  1         2  
  1         40  
9              
10             BEGIN {
11 1     1   5 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 0     0 0   my $self = shift;
64 0           my ( $node, $ctx ) = pos_validated_list(
65             \@_,
66             { isa => 'XML::LibXML::Node' },
67             { isa => 'PRANG::Graph::Context' },
68             );
69            
70 0           for my $choice ( @{ $self->choices } ) {
  0            
71 0 0         if ( defined $choice->node_ok($node, $ctx) ) {
72 0           return 1;
73             }
74             }
75 0           return;
76             }
77              
78             sub accept {
79 0     0 0   my $self = shift;
80 0           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 0 0         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 0           $ctx->exception(
94             "Single child node expected, multiple found",
95             $node,
96             );
97             }
98              
99 0           my $num;
100 0 0         my $name = $node->isa("XML::LibXML::Text")
101             ? ""
102             : $node->localname;
103 0   0       my $xmlns = length($name) && $node->namespaceURI;
104 0           my ($key, $val, $x, $ns);
105 0           for my $choice ( @{ $self->choices } ) {
  0            
106 0           $num++;
107 0 0         if ( defined $choice->node_ok($node, $ctx) ) {
108 0           ($key, $val, $x, $ns) = $choice->accept($node, $ctx, $lax);
109             }
110 0 0         if ($key) {
111 0           $ctx->chosen($num);
112             return (
113 0   0       $key, $val, $x||eval{$choice->nodeName}||"",
114             $ns
115             );
116             }
117             }
118 0           return ();
119             }
120              
121             sub complete {
122 0     0 0   my $self = shift;
123 0           my ( $ctx ) = pos_validated_list(
124             \@_,
125             { isa => 'PRANG::Graph::Context' },
126             );
127            
128 0           $ctx->chosen;
129             }
130              
131             sub expected {
132 0     0 0   my $self = shift;
133 0           my ( $ctx ) = pos_validated_list(
134             \@_,
135             { isa => 'PRANG::Graph::Context' },
136             );
137            
138 0 0         if ( my $num = $ctx->chosen ) {
139 0           return $self->choices->[$num-1]->expected($ctx);
140             }
141             else {
142 0           my @choices;
143 0           for my $choice ( @{$self->choices} ) {
  0            
144 0           push @choices, $choice->expected($ctx);
145             }
146 0           return @choices;
147             }
148             }
149              
150             our $REGISTRY =
151             Moose::Util::TypeConstraints::get_type_constraint_registry();
152              
153             sub output {
154 0     0 0   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 0           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 0           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 0           my $an = $self->attrName;
174 0   0       $value //= $item->$an;
175 0           my ($name, $xmlns);
176 0 0 0       if ( $self->has_name_attr || $self->has_xmlns_attr ) {
    0          
177 0 0         if ( $self->has_name_attr ) {
178 0           my $x = $self->name_attr;
179 0           $name = $item->$x;
180 0 0         if ( defined $slot ) {
181 0           $name = $name->[$slot];
182             }
183             }
184             else {
185 0   0       $name = $self->xml_nodeName // $an;
186             }
187 0 0         if ( $self->has_xmlns_attr ) {
188 0           my $attr_getter = $self->xmlns_attr;
189 0           $xmlns = $item->$attr_getter;
190 0 0         if ( defined $slot ) {
191 0           $xmlns = $xmlns->[$slot];
192             }
193             }
194             else {
195 0   0       $xmlns = $self->xmlns // "";
196             }
197             }
198             elsif ( $self->has_type_map ) {
199 0           my $map = $self->type_map;
200 0           my $nodeCount = 0;
201 0           for my $element ( keys %$map ) {
202 0           my $type = $map->{$element};
203 0 0         if ( !ref $type ) {
204 0           $type = $map->{$element} =
205             $REGISTRY->get_type_constraint($type);
206             }
207            
208 0 0         if ( $type->check($value) ) {
209 0           $name = $element;
210 0           $nodeCount++;
211             }
212            
213 0 0         if ($nodeCount > 1) {
214 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 0 0         if ( !defined $name ) {
221 0           $ctx->exception(
222             "don't know what to serialize $value to for slot "
223             .$self->attrName
224             );
225             }
226 0 0         if ( length $name ) {
227 0 0 0       if ( $self->has_type_map_prefix and $name =~ /(.*):(.*)/) {
228 0           $name = $2;
229 0           $xmlns = $self->type_map_prefix->{$1};
230             }
231 0           my $found;
232 0           for my $choice ( @{ $self->choices } ) {
  0            
233 0 0         if ($xmlns) {
234 0 0         next unless $choice->has_xmlns;
235 0 0 0       next unless $choice->xmlns eq $xmlns or
236             $choice->xmlns eq "*";
237             }
238 0 0 0       next unless $choice->nodeName eq $name or
239             $choice->nodeName eq "*";
240 0           $found++;
241 0 0         $choice->output(
    0          
242             $item,$node,$ctx,
243             value => $value,
244             (defined $slot ? (slot => $slot) : ()),
245             name => $name,
246             (defined $xmlns ? (xmlns => $xmlns) : ()),
247             );
248 0           last;
249             }
250 0 0         if ( !$found ) {
251 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 0           my $tn = $self->createTextNode($node->ownerDocument, $value);
266 0           $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