File Coverage

blib/lib/YAML/PP/Constructor.pm
Criterion Covered Total %
statement 231 234 98.7
branch 73 82 89.0
condition 39 49 79.5
subroutine 43 44 97.7
pod 23 34 67.6
total 409 443 92.3


line stmt bran cond sub pod time code
1             # ABSTRACT: Construct data structure from Parser Events
2 42     42   150635 use strict;
  42         65  
  42         1284  
3 42     42   153 use warnings;
  42         95  
  42         2898  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = 'v0.39.0'; # VERSION
7              
8 42     42   1875 use YAML::PP;
  42         63  
  42         1307  
9 42         2464 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 42     42   197 /;
  42         82  
12 42     42   226 use Scalar::Util qw/ reftype /;
  42         61  
  42         1725  
13 42     42   191 use Carp qw/ croak /;
  42         62  
  42         2529  
14              
15 42 50 33 42   189 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  42         53  
  42         3670  
16 42 50   42   216 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  42         82  
  42         100810  
17              
18             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
19              
20             sub new {
21 756     756 1 2482 my ($class, %args) = @_;
22              
23 756         1286 my $default_yaml_version = delete $args{default_yaml_version};
24 756         1114 my $duplicate_keys = delete $args{duplicate_keys};
25 756 100       1375 unless (defined $duplicate_keys) {
26 443         648 $duplicate_keys = 0;
27             }
28 756         955 my $require_footer = delete $args{require_footer};
29 756   100     2036 my $preserve = delete $args{preserve} || 0;
30 756 100       1627 if ($preserve == 1) {
31 1         2 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
32             }
33 756   50     1476 my $cyclic_refs = delete $args{cyclic_refs} || 'fatal';
34             die "Invalid value for cyclic_refs: $cyclic_refs"
35 756 100       2045 unless $cyclic_refs{ $cyclic_refs };
36 755         1015 my $schemas = delete $args{schemas};
37              
38 755 50       1457 if (keys %args) {
39 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
40             }
41              
42 755         3697 my $self = bless {
43             default_yaml_version => $default_yaml_version,
44             schemas => $schemas,
45             cyclic_refs => $cyclic_refs,
46             preserve => $preserve,
47             duplicate_keys => $duplicate_keys,
48             require_footer => $require_footer,
49             }, $class;
50 755         1972 $self->init;
51 755         2199 return $self;
52             }
53              
54             sub clone {
55 9     9 0 21 my ($self) = @_;
56             my $clone = {
57             schemas => $self->{schemas},
58             schema => $self->{schema},
59             default_yaml_version => $self->{default_yaml_version},
60             cyclic_refs => $self->cyclic_refs,
61             preserve => $self->{preserve},
62 9         30 };
63 9         53 return bless $clone, ref $self;
64             }
65              
66             sub init {
67 2952     2952 1 4559 my ($self) = @_;
68 2952         6815 $self->set_docs([]);
69 2952         6155 $self->set_stack([]);
70 2952         5881 $self->set_anchors({});
71 2952         6051 $self->set_yaml_version($self->default_yaml_version);
72 2952         5026 $self->set_schema($self->schemas->{ $self->yaml_version } );
73             }
74              
75 4453     4453 1 6252 sub docs { return $_[0]->{docs} }
76 17428     17428 1 25476 sub stack { return $_[0]->{stack} }
77 588     588 1 1496 sub anchors { return $_[0]->{anchors} }
78 2952     2952 1 5810 sub set_docs { $_[0]->{docs} = $_[1] }
79 5236     5236 1 11926 sub set_stack { $_[0]->{stack} = $_[1] }
80 5236     5236 1 8851 sub set_anchors { $_[0]->{anchors} = $_[1] }
81 2986     2986 0 5864 sub schemas { return $_[0]->{schemas} }
82 10187     10187 1 27755 sub schema { return $_[0]->{schema} }
83 2986     2986 1 5558 sub set_schema { $_[0]->{schema} = $_[1] }
84 18     18 1 53 sub cyclic_refs { return $_[0]->{cyclic_refs} }
85 0     0 1 0 sub set_cyclic_refs { $_[0]->{cyclic_refs} = $_[1] }
86 2952     2952 0 6719 sub yaml_version { return $_[0]->{yaml_version} }
87 2986     2986 0 4610 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
88 2976     2976 0 7590 sub default_yaml_version { return $_[0]->{default_yaml_version} }
89 1313     1313 0 2438 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
90 7702     7702 0 10251 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
91 2526     2526 0 3959 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
92 10222     10222 0 15829 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
93 5     5 0 15 sub duplicate_keys { return $_[0]->{duplicate_keys} }
94 2127     2127 0 5691 sub require_footer { return $_[0]->{require_footer} }
95              
96             sub document_start_event {
97 2315     2315 1 3460 my ($self, $event) = @_;
98 2315         4831 my $stack = $self->stack;
99 2315 100       4279 if ($event->{version_directive}) {
100 34         54 my $version = $event->{version_directive};
101 34         79 $version = "$version->{major}.$version->{minor}";
102 34 100       85 if ($self->{schemas}->{ $version }) {
103 22         80 $self->set_yaml_version($version);
104 22         38 $self->set_schema($self->schemas->{ $version });
105             }
106             else {
107 12         31 $self->set_yaml_version($self->default_yaml_version);
108 12         37 $self->set_schema($self->schemas->{ $self->default_yaml_version });
109             }
110             }
111 2315         2982 my $ref = [];
112 2315         9211 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
113             }
114              
115             sub document_end_event {
116 2287     2287 1 3510 my ($self, $event) = @_;
117 2287         3885 my $stack = $self->stack;
118 2287         3302 my $last = pop @$stack;
119 2287 50       4794 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
120 2287 50       4257 if (@$stack) {
121 0         0 die "Got unexpected end of document";
122             }
123 2287         3889 my $docs = $self->docs;
124 2287 100 100     6593 if ($event->{implicit} and $self->require_footer) {
125 3         36 die sprintf "load: Document (%d) did not end with '...' (require_footer=1)", 1 + scalar @$docs;
126             }
127 2284         4604 push @$docs, $last->{ref}->[0];
128 2284         5189 $self->set_anchors({});
129 2284         4323 $self->set_stack([]);
130             }
131              
132             sub mapping_start_event {
133 1313     1313 1 2080 my ($self, $event) = @_;
134 1313         2609 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
135 1313         5359 my $ref = {
136             type => 'mapping',
137             ref => [],
138             data => \$data,
139             event => $event,
140             on_data => $on_data,
141             };
142 1313         2537 my $stack = $self->stack;
143              
144 1313         2625 my $preserve_order = $self->preserve_order;
145 1313         2363 my $preserve_style = $self->preserve_flow_style;
146 1313         2235 my $preserve_alias = $self->preserve_alias;
147 1313 100 100     5989 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
148 24         143 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
149             }
150 1313 100       2315 if ($preserve_style) {
151 15         23 my $t = tied %$data;
152 15         31 $t->{style} = $event->{style};
153             }
154              
155 1313         1904 push @$stack, $ref;
156 1313 100       4502 if (defined(my $anchor = $event->{anchor})) {
157 77 100       144 if ($preserve_alias) {
158 6         8 my $t = tied %$data;
159 6 100       12 unless (exists $self->anchors->{ $anchor }) {
160             # Repeated anchors cannot be preserved
161 5         11 $t->{alias} = $anchor;
162             }
163             }
164 77         222 $self->anchors->{ $anchor } = { data => $ref->{data} };
165             }
166             }
167              
168             sub mapping_end_event {
169 1301     1301 1 2157 my ($self, $event) = @_;
170 1301         2330 my $stack = $self->stack;
171              
172 1301         1935 my $last = pop @$stack;
173 1301         1911 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1301         2467  
174 1301 50       2698 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
175              
176 1301         2023 my @merge_keys;
177             my @ref;
178 1301         2793 for (my $i = 0; $i < @$ref; $i += 2) {
179 2524         3366 my $key = $ref->[ $i ];
180 2524 100       3778 if (ref $key eq 'YAML::PP::Type::MergeKey') {
181 6         9 my $merge = $ref->[ $i + 1 ];
182 6 100 100     30 if ((reftype($merge) || '') eq 'HASH') {
    100 100        
183 1         3 push @merge_keys, $merge;
184             }
185             elsif ((reftype($merge) || '') eq 'ARRAY') {
186 4         7 for my $item (@$merge) {
187 7 100 100     15 if ((reftype($item) || '') eq 'HASH') {
188 5         9 push @merge_keys, $item;
189             }
190             else {
191 2         23 die "Expected hash for merge key";
192             }
193             }
194             }
195             else {
196 1         13 die "Expected hash or array for merge key";
197             }
198             }
199             else {
200 2518         6024 push @ref, $key, $ref->[ $i + 1 ];
201             }
202             }
203 1298         2268 for my $merge (@merge_keys) {
204 6         13 for my $key (keys %$merge) {
205 9 100       15 unless (exists $$data->{ $key }) {
206 8         183 $$data->{ $key } = $merge->{ $key };
207             }
208             }
209             }
210             my $on_data = $last->{on_data} || sub {
211 1228     1228   1950 my ($self, $hash, $items) = @_;
212 1228         1425 my %seen;
213 1228         2645 for (my $i = 0; $i < @$items; $i += 2) {
214 2398         4517 my ($key, $value) = @$items[ $i, $i + 1 ];
215 2398         3506 $key = '' unless defined $key;
216 2398         3476 if (ref $key) {
217 71         109 $key = $self->stringify_complex($key);
218             }
219 2398         6697 if ($seen{ $key }++ and not $self->duplicate_keys) {
220 2         295 croak "Duplicate key '$key'";
221             }
222 2396         6540 $$hash->{ $key } = $value;
223             }
224 1298   66     8390 };
225 1298         3429 $on_data->($self, $data, \@ref);
226 1286         1551 push @{ $stack->[-1]->{ref} }, $$data;
  1286         3027  
227 1286 100       2952 if (defined(my $anchor = $last->{event}->{anchor})) {
228 73         155 $self->anchors->{ $anchor }->{finished} = 1;
229             }
230 1286         10995 return;
231             }
232              
233             sub sequence_start_event {
234 1213     1213 1 1856 my ($self, $event) = @_;
235 1213         2120 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
236 1213         4983 my $ref = {
237             type => 'sequence',
238             ref => [],
239             data => \$data,
240             event => $event,
241             on_data => $on_data,
242             };
243 1213         2298 my $stack = $self->stack;
244              
245 1213         2125 my $preserve_style = $self->preserve_flow_style;
246 1213         2028 my $preserve_alias = $self->preserve_alias;
247 1213 100 66     3542 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
248 9         60 tie @$data, 'YAML::PP::Preserve::Array', @$data;
249 9         17 my $t = tied @$data;
250 9         29 $t->{style} = $event->{style};
251             }
252              
253 1213         1704 push @$stack, $ref;
254 1213 100       4161 if (defined(my $anchor = $event->{anchor})) {
255 30 100       72 if ($preserve_alias) {
256 6         9 my $t = tied @$data;
257 6 100       12 unless (exists $self->anchors->{ $anchor }) {
258             # Repeated anchors cannot be preserved
259 5         10 $t->{alias} = $anchor;
260             }
261             }
262 30         139 $self->anchors->{ $anchor } = { data => $ref->{data} };
263             }
264             }
265              
266             sub sequence_end_event {
267 1205     1205 1 1965 my ($self, $event) = @_;
268 1205         1854 my $stack = $self->stack;
269 1205         1850 my $last = pop @$stack;
270 1205 50       2713 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
271 1205         1625 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1205         2308  
272              
273             my $on_data = $last->{on_data} || sub {
274 1190     1190   1857 my ($self, $array, $items) = @_;
275 1190         2799 push @$$array, @$items;
276 1205   66     6782 };
277 1205         2595 $on_data->($self, $data, $ref);
278 1205         1344 push @{ $stack->[-1]->{ref} }, $$data;
  1205         2391  
279 1205 100       2824 if (defined(my $anchor = $last->{event}->{anchor})) {
280 30         57 my $test = $self->anchors->{ $anchor };
281 30         63 $self->anchors->{ $anchor }->{finished} = 1;
282             }
283 1205         6275 return;
284             }
285              
286       2197 1   sub stream_start_event {}
287              
288       2166 1   sub stream_end_event {}
289              
290             sub scalar_event {
291 7661     7661 1 10373 my ($self, $event) = @_;
292 7661         7762 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
293 7661         12484 my $value = $self->schema->load_scalar($self, $event);
294 7652         14414 my $last = $self->stack->[-1];
295 7652         12285 my $preserve_alias = $self->preserve_alias;
296 7652         11520 my $preserve_style = $self->preserve_scalar_style;
297 7652 100 100     23624 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
298             my %args = (
299             value => $value,
300             tag => $event->{tag},
301 83         225 );
302 83 100       152 if ($preserve_style) {
303 17         33 $args{style} = $event->{style};
304             }
305 83 100 100     206 if ($preserve_alias and defined $event->{anchor}) {
306 6         12 my $anchor = $event->{anchor};
307 6 100       13 unless (exists $self->anchors->{ $anchor }) {
308             # Repeated anchors cannot be preserved
309 5         11 $args{alias} = $event->{anchor};
310             }
311             }
312 83         283 $value = YAML::PP::Preserve::Scalar->new( %args );
313             }
314 7652 100       14713 if (defined (my $name = $event->{anchor})) {
315 185         580 $self->anchors->{ $name } = { data => \$value, finished => 1 };
316             }
317 7652         8562 push @{ $last->{ref} }, $value;
  7652         20261  
318             }
319              
320             sub alias_event {
321 145     145 1 251 my ($self, $event) = @_;
322 145         207 my $value;
323 145         245 my $name = $event->{value};
324 145 100       272 if (my $anchor = $self->anchors->{ $name }) {
325             # We know this is a cyclic ref since the node hasn't
326             # been constructed completely yet
327 144 100       314 unless ($anchor->{finished} ) {
328 9         25 my $cyclic_refs = $self->cyclic_refs;
329 9 100       27 if ($cyclic_refs ne 'allow') {
330 4 100       8 if ($cyclic_refs eq 'fatal') {
331 2         41 croak "Found cyclic ref for alias '$name'";
332             }
333 2 100       38 if ($cyclic_refs eq 'warn') {
    50          
334 1         7 $anchor = { data => \undef };
335 1         26 warn "Found cyclic ref for alias '$name'";
336             }
337             elsif ($cyclic_refs eq 'ignore') {
338 1         5 $anchor = { data => \undef };
339             }
340             }
341             }
342 142         317 $value = $anchor->{data};
343             }
344             else {
345 1         153 croak "No anchor defined for alias '$name'";
346             }
347 142         253 my $last = $self->stack->[-1];
348 142         212 push @{ $last->{ref} }, $$value;
  142         384  
349             }
350              
351             sub stringify_complex {
352 75     75 1 100 my ($self, $data) = @_;
353 75 50 66     138 return $data if (
      66        
354             ref $data eq 'YAML::PP::Preserve::Scalar'
355             and ($self->preserve_scalar_style or $self->preserve_alias)
356             );
357 25         129 require Data::Dumper;
358 25         38 local $Data::Dumper::Quotekeys = 0;
359 25         28 local $Data::Dumper::Terse = 1;
360 25         40 local $Data::Dumper::Indent = 0;
361 25         32 local $Data::Dumper::Useqq = 0;
362 25         42 local $Data::Dumper::Sortkeys = 1;
363 25         129 my $string = Data::Dumper->Dump([$data], ['data']);
364 25         1330 $string =~ s/^\$data = //;
365 25         119 return $string;
366             }
367              
368             1;
369              
370             __END__
371              
372             =pod
373              
374             =encoding utf-8
375              
376             =head1 NAME
377              
378             YAML::PP::Constructor - Constructing data structure from parsing events
379              
380             =head1 METHODS
381              
382             =over
383              
384             =item new
385              
386             The Constructor constructor
387              
388             my $constructor = YAML::PP::Constructor->new(
389             schema => $schema,
390             cyclic_refs => $cyclic_refs,
391             );
392              
393             =item init
394              
395             Resets any data being used during construction.
396              
397             $constructor->init;
398              
399             =item document_start_event, document_end_event, mapping_start_event, mapping_end_event, sequence_start_event, sequence_end_event, scalar_event, alias_event, stream_start_event, stream_end_event
400              
401             These methods are called from L<YAML::PP::Parser>:
402              
403             $constructor->document_start_event($event);
404              
405             =item anchors, set_anchors
406              
407             Helper for storing anchors during construction
408              
409             =item docs, set_docs
410              
411             Helper for storing resulting documents during construction
412              
413             =item stack, set_stack
414              
415             Helper for storing data during construction
416              
417             =item cyclic_refs, set_cyclic_refs
418              
419             Option for controlling the behaviour when finding circular references
420              
421             =item schema, set_schema
422              
423             Holds a L<YAML::PP::Schema> object
424              
425             =item stringify_complex
426              
427             When constructing a hash and getting a non-scalar key, this method is
428             used to stringify the key.
429              
430             It uses a terse Data::Dumper output. Other modules, like L<YAML::XS>, use
431             the default stringification, C<ARRAY(0x55617c0c7398)> for example.
432              
433             =back
434              
435             =cut