File Coverage

blib/lib/YAML/PP/Constructor.pm
Criterion Covered Total %
statement 243 246 98.7
branch 75 84 89.2
condition 42 53 79.2
subroutine 46 47 97.8
pod 23 35 65.7
total 429 465 92.2


line stmt bran cond sub pod time code
1             # ABSTRACT: Construct data structure from Parser Events
2 43     43   164091 use strict;
  43         78  
  43         1319  
3 43     43   146 use warnings;
  43         61  
  43         2617  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = 'v0.40.0'; # VERSION
7              
8 43     43   2134 use YAML::PP;
  43         61  
  43         1305  
9 43         2406 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 43     43   166 /;
  43         107  
12 43     43   244 use Scalar::Util qw/ reftype /;
  43         74  
  43         1638  
13 43     43   180 use Carp qw/ croak /;
  43         54  
  43         2597  
14              
15 43 50 33 43   188 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  43         73  
  43         3645  
16 43 50   43   214 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  43         73  
  43         2211  
17              
18 43     43   194 use constant MAX_DEPTH => 2 ** 9;
  43         66  
  43         107861  
19              
20             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
21              
22             sub new {
23 757     757 1 2362 my ($class, %args) = @_;
24              
25 757         1282 my $default_yaml_version = delete $args{default_yaml_version};
26 757         1226 my $duplicate_keys = delete $args{duplicate_keys};
27 757 100       1385 unless (defined $duplicate_keys) {
28 444         509 $duplicate_keys = 0;
29             }
30 757         959 my $require_footer = delete $args{require_footer};
31 757   100     2096 my $max_depth = delete $args{max_depth} || MAX_DEPTH;
32 757   100     1738 my $preserve = delete $args{preserve} || 0;
33 757 100       1462 if ($preserve == 1) {
34 1         2 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
35             }
36 757   50     1413 my $cyclic_refs = delete $args{cyclic_refs} || 'fatal';
37             die "Invalid value for cyclic_refs: $cyclic_refs"
38 757 100       1809 unless $cyclic_refs{ $cyclic_refs };
39 756         1041 my $schemas = delete $args{schemas};
40              
41 756 50       1410 if (keys %args) {
42 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
43             }
44              
45 756         3572 my $self = bless {
46             default_yaml_version => $default_yaml_version,
47             schemas => $schemas,
48             cyclic_refs => $cyclic_refs,
49             preserve => $preserve,
50             duplicate_keys => $duplicate_keys,
51             require_footer => $require_footer,
52             max_depth => $max_depth,
53             }, $class;
54 756         1871 $self->init;
55 756         2225 return $self;
56             }
57              
58             sub clone {
59 9     9 0 14 my ($self) = @_;
60             my $clone = {
61             schemas => $self->{schemas},
62             schema => $self->{schema},
63             default_yaml_version => $self->{default_yaml_version},
64             cyclic_refs => $self->cyclic_refs,
65             preserve => $self->{preserve},
66             max_depth => $self->{max_depth},
67 9         23 };
68 9         31 return bless $clone, ref $self;
69             }
70              
71             sub init {
72 2958     2958 1 4554 my ($self) = @_;
73 2958         7228 $self->set_docs([]);
74 2958         6113 $self->set_stack([]);
75 2958         6433 $self->set_anchors({});
76 2958         5576 $self->set_yaml_version($self->default_yaml_version);
77 2958         5074 $self->set_schema($self->schemas->{ $self->yaml_version } );
78             }
79              
80 4455     4455 1 6222 sub docs { return $_[0]->{docs} }
81 20021     20021 1 28237 sub stack { return $_[0]->{stack} }
82 588     588 1 1486 sub anchors { return $_[0]->{anchors} }
83 2958     2958 1 5935 sub set_docs { $_[0]->{docs} = $_[1] }
84 5243     5243 1 11993 sub set_stack { $_[0]->{stack} = $_[1] }
85 5243     5243 1 8198 sub set_anchors { $_[0]->{anchors} = $_[1] }
86 2992     2992 0 5806 sub schemas { return $_[0]->{schemas} }
87 10216     10216 1 26319 sub schema { return $_[0]->{schema} }
88 2992     2992 1 5467 sub set_schema { $_[0]->{schema} = $_[1] }
89 18     18 1 51 sub cyclic_refs { return $_[0]->{cyclic_refs} }
90 0     0 1 0 sub set_cyclic_refs { $_[0]->{cyclic_refs} = $_[1] }
91 2958     2958 0 6595 sub yaml_version { return $_[0]->{yaml_version} }
92 2992     2992 0 4687 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
93 2982     2982 0 6942 sub default_yaml_version { return $_[0]->{default_yaml_version} }
94 1323     1323 0 2323 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
95 7702     7702 0 10014 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
96 2551     2551 0 3638 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
97 10247     10247 0 15898 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
98 5     5 0 14 sub duplicate_keys { return $_[0]->{duplicate_keys} }
99 2128     2128 0 5618 sub require_footer { return $_[0]->{require_footer} }
100 2559     2559 0 8501 sub max_depth { return $_[0]->{max_depth} }
101              
102             sub document_start_event {
103 2320     2320 1 3506 my ($self, $event) = @_;
104 2320         3921 my $stack = $self->stack;
105 2320 100       4445 if ($event->{version_directive}) {
106 34         53 my $version = $event->{version_directive};
107 34         68 $version = "$version->{major}.$version->{minor}";
108 34 100       94 if ($self->{schemas}->{ $version }) {
109 22         68 $self->set_yaml_version($version);
110 22         38 $self->set_schema($self->schemas->{ $version });
111             }
112             else {
113 12         28 $self->set_yaml_version($self->default_yaml_version);
114 12         22 $self->set_schema($self->schemas->{ $self->default_yaml_version });
115             }
116             }
117 2320         2943 my $ref = [];
118 2320         8853 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
119             }
120              
121             sub document_end_event {
122 2288     2288 1 3248 my ($self, $event) = @_;
123 2288         3483 my $stack = $self->stack;
124 2288         3005 my $last = pop @$stack;
125 2288 50       4519 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
126 2288 50       4018 if (@$stack) {
127 0         0 die "Got unexpected end of document";
128             }
129 2288         3716 my $docs = $self->docs;
130 2288 100 100     14498 if ($event->{implicit} and $self->require_footer) {
131 3         43 die sprintf "load: Document (%d) did not end with '...' (require_footer=1)", 1 + scalar @$docs;
132             }
133 2285         4338 push @$docs, $last->{ref}->[0];
134 2285         5193 $self->set_anchors({});
135 2285         3802 $self->set_stack([]);
136             }
137              
138             sub _check_depth {
139 2555     2555   3364 my ($self) = @_;
140 2555         3636 my $stack = $self->stack;
141 2555         3259 my $c = @$stack;
142 2555 100 50     3949 if ($c > ($self->max_depth || MAX_DEPTH)){
143 4         6 croak sprintf 'Depth of nesting exceeds maximum %s', $self->max_depth;
144             }
145             }
146              
147             sub mapping_start_event {
148 1325     1325 1 2023 my ($self, $event) = @_;
149 1325         2448 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
150 1325         4984 my $ref = {
151             type => 'mapping',
152             ref => [],
153             data => \$data,
154             event => $event,
155             on_data => $on_data,
156             };
157 1325         2799 $self->_check_depth;
158 1323         1947 my $stack = $self->stack;
159              
160 1323         2264 my $preserve_order = $self->preserve_order;
161 1323         2053 my $preserve_style = $self->preserve_flow_style;
162 1323         2037 my $preserve_alias = $self->preserve_alias;
163 1323 100 100     5527 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
164 24         166 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
165             }
166 1323 100       2108 if ($preserve_style) {
167 15         28 my $t = tied %$data;
168 15         43 $t->{style} = $event->{style};
169             }
170              
171 1323         1767 push @$stack, $ref;
172 1323 100       3947 if (defined(my $anchor = $event->{anchor})) {
173 77 100       139 if ($preserve_alias) {
174 6         7 my $t = tied %$data;
175 6 100       14 unless (exists $self->anchors->{ $anchor }) {
176             # Repeated anchors cannot be preserved
177 5         11 $t->{alias} = $anchor;
178             }
179             }
180 77         200 $self->anchors->{ $anchor } = { data => $ref->{data} };
181             }
182             }
183              
184             sub mapping_end_event {
185 1301     1301 1 1998 my ($self, $event) = @_;
186 1301         1912 my $stack = $self->stack;
187              
188 1301         1712 my $last = pop @$stack;
189 1301         1591 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1301         2377  
190 1301 50       2521 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
191              
192 1301         2022 my @merge_keys;
193             my @ref;
194 1301         2605 for (my $i = 0; $i < @$ref; $i += 2) {
195 2524         3233 my $key = $ref->[ $i ];
196 2524 100       3260 if (ref $key eq 'YAML::PP::Type::MergeKey') {
197 6         9 my $merge = $ref->[ $i + 1 ];
198 6 100 100     27 if ((reftype($merge) || '') eq 'HASH') {
    100 100        
199 1         2 push @merge_keys, $merge;
200             }
201             elsif ((reftype($merge) || '') eq 'ARRAY') {
202 4         7 for my $item (@$merge) {
203 7 100 100     15 if ((reftype($item) || '') eq 'HASH') {
204 5         8 push @merge_keys, $item;
205             }
206             else {
207 2         21 die "Expected hash for merge key";
208             }
209             }
210             }
211             else {
212 1         11 die "Expected hash or array for merge key";
213             }
214             }
215             else {
216 2518         5528 push @ref, $key, $ref->[ $i + 1 ];
217             }
218             }
219 1298         2041 for my $merge (@merge_keys) {
220 6         29 for my $key (keys %$merge) {
221 9 100       13 unless (exists $$data->{ $key }) {
222 8         15 $$data->{ $key } = $merge->{ $key };
223             }
224             }
225             }
226             my $on_data = $last->{on_data} || sub {
227 1228     1228   1847 my ($self, $hash, $items) = @_;
228 1228         1466 my %seen;
229 1228         2364 for (my $i = 0; $i < @$items; $i += 2) {
230 2398         4115 my ($key, $value) = @$items[ $i, $i + 1 ];
231 2398         3283 $key = '' unless defined $key;
232 2398         3112 if (ref $key) {
233 71         130 $key = $self->stringify_complex($key);
234             }
235 2398         5977 if ($seen{ $key }++ and not $self->duplicate_keys) {
236 2         275 croak "Duplicate key '$key'";
237             }
238 2396         5916 $$hash->{ $key } = $value;
239             }
240 1298   66     7941 };
241 1298         3035 $on_data->($self, $data, \@ref);
242 1286         1481 push @{ $stack->[-1]->{ref} }, $$data;
  1286         2647  
243 1286 100       2559 if (defined(my $anchor = $last->{event}->{anchor})) {
244 73         135 $self->anchors->{ $anchor }->{finished} = 1;
245             }
246 1286         10361 return;
247             }
248              
249             sub sequence_start_event {
250 1230     1230 1 1756 my ($self, $event) = @_;
251 1230         1993 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
252 1230         4383 my $ref = {
253             type => 'sequence',
254             ref => [],
255             data => \$data,
256             event => $event,
257             on_data => $on_data,
258             };
259 1230         2166 my $stack = $self->stack;
260 1230         2583 $self->_check_depth;
261              
262 1228         2062 my $preserve_style = $self->preserve_flow_style;
263 1228         1815 my $preserve_alias = $self->preserve_alias;
264 1228 100 66     3154 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
265 9         57 tie @$data, 'YAML::PP::Preserve::Array', @$data;
266 9         15 my $t = tied @$data;
267 9         37 $t->{style} = $event->{style};
268             }
269              
270 1228         1584 push @$stack, $ref;
271 1228 100       3674 if (defined(my $anchor = $event->{anchor})) {
272 30 100       77 if ($preserve_alias) {
273 6         9 my $t = tied @$data;
274 6 100       11 unless (exists $self->anchors->{ $anchor }) {
275             # Repeated anchors cannot be preserved
276 5         11 $t->{alias} = $anchor;
277             }
278             }
279 30         113 $self->anchors->{ $anchor } = { data => $ref->{data} };
280             }
281             }
282              
283             sub sequence_end_event {
284 1210     1210 1 1700 my ($self, $event) = @_;
285 1210         1839 my $stack = $self->stack;
286 1210         1673 my $last = pop @$stack;
287 1210 50       2412 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
288 1210         1593 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1210         2145  
289              
290             my $on_data = $last->{on_data} || sub {
291 1195     1195   1687 my ($self, $array, $items) = @_;
292 1195         2651 push @$$array, @$items;
293 1210   66     6559 };
294 1210         2481 $on_data->($self, $data, $ref);
295 1210         1468 push @{ $stack->[-1]->{ref} }, $$data;
  1210         2386  
296 1210 100       2420 if (defined(my $anchor = $last->{event}->{anchor})) {
297 30         55 my $test = $self->anchors->{ $anchor };
298 30         52 $self->anchors->{ $anchor }->{finished} = 1;
299             }
300 1210         6032 return;
301             }
302              
303       2202 1   sub stream_start_event {}
304              
305       2167 1   sub stream_end_event {}
306              
307             sub scalar_event {
308 7661     7661 1 9809 my ($self, $event) = @_;
309 7661         7386 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
310 7661         12402 my $value = $self->schema->load_scalar($self, $event);
311 7652         14262 my $last = $self->stack->[-1];
312 7652         11299 my $preserve_alias = $self->preserve_alias;
313 7652         10813 my $preserve_style = $self->preserve_scalar_style;
314 7652 100 100     22483 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
315             my %args = (
316             value => $value,
317             tag => $event->{tag},
318 83         212 );
319 83 100       127 if ($preserve_style) {
320 17         26 $args{style} = $event->{style};
321             }
322 83 100 100     198 if ($preserve_alias and defined $event->{anchor}) {
323 6         11 my $anchor = $event->{anchor};
324 6 100       11 unless (exists $self->anchors->{ $anchor }) {
325             # Repeated anchors cannot be preserved
326 5         9 $args{alias} = $event->{anchor};
327             }
328             }
329 83         294 $value = YAML::PP::Preserve::Scalar->new( %args );
330             }
331 7652 100       13894 if (defined (my $name = $event->{anchor})) {
332 185         572 $self->anchors->{ $name } = { data => \$value, finished => 1 };
333             }
334 7652         7808 push @{ $last->{ref} }, $value;
  7652         19385  
335             }
336              
337             sub alias_event {
338 145     145 1 319 my ($self, $event) = @_;
339 145         271 my $value;
340 145         225 my $name = $event->{value};
341 145 100       259 if (my $anchor = $self->anchors->{ $name }) {
342             # We know this is a cyclic ref since the node hasn't
343             # been constructed completely yet
344 144 100       318 unless ($anchor->{finished} ) {
345 9         53 my $cyclic_refs = $self->cyclic_refs;
346 9 100       25 if ($cyclic_refs ne 'allow') {
347 4 100       10 if ($cyclic_refs eq 'fatal') {
348 2         34 croak "Found cyclic ref for alias '$name'";
349             }
350 2 100       14 if ($cyclic_refs eq 'warn') {
    50          
351 1         4 $anchor = { data => \undef };
352 1         21 warn "Found cyclic ref for alias '$name'";
353             }
354             elsif ($cyclic_refs eq 'ignore') {
355 1         3 $anchor = { data => \undef };
356             }
357             }
358             }
359 142         244 $value = $anchor->{data};
360             }
361             else {
362 1         153 croak "No anchor defined for alias '$name'";
363             }
364 142         272 my $last = $self->stack->[-1];
365 142         191 push @{ $last->{ref} }, $$value;
  142         352  
366             }
367              
368             sub stringify_complex {
369 75     75 1 100 my ($self, $data) = @_;
370 75 50 66     140 return $data if (
      66        
371             ref $data eq 'YAML::PP::Preserve::Scalar'
372             and ($self->preserve_scalar_style or $self->preserve_alias)
373             );
374 25         132 require Data::Dumper;
375 25         48 local $Data::Dumper::Quotekeys = 0;
376 25         26 local $Data::Dumper::Terse = 1;
377 25         39 local $Data::Dumper::Indent = 0;
378 25         87 local $Data::Dumper::Useqq = 0;
379 25         54 local $Data::Dumper::Sortkeys = 1;
380 25         185 my $string = Data::Dumper->Dump([$data], ['data']);
381 25         1198 $string =~ s/^\$data = //;
382 25         58 return $string;
383             }
384              
385             1;
386              
387             __END__