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 48     48   158331 use strict;
  48         69  
  48         1446  
3 48     48   181 use warnings;
  48         66  
  48         2867  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = 'v0.41.0'; # VERSION
7              
8 48     48   2190 use YAML::PP;
  48         71  
  48         1386  
9 48         2577 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 48     48   177 /;
  48         83  
12 48     48   267 use Scalar::Util qw/ reftype /;
  48         75  
  48         1959  
13 48     48   212 use Carp qw/ croak /;
  48         67  
  48         2755  
14              
15 48 50 33 48   205 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  48         60  
  48         4143  
16 48 50   48   233 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  48         74  
  48         2380  
17              
18 48     48   183 use constant MAX_DEPTH => 2 ** 9;
  48         109  
  48         119837  
19              
20             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
21              
22             sub new {
23 766     766 1 2546 my ($class, %args) = @_;
24              
25 766         1093 my $default_yaml_version = delete $args{default_yaml_version};
26 766         1042 my $duplicate_keys = delete $args{duplicate_keys};
27 766 100       1281 unless (defined $duplicate_keys) {
28 453         518 $duplicate_keys = 0;
29             }
30 766         905 my $require_footer = delete $args{require_footer};
31 766   100     1860 my $max_depth = delete $args{max_depth} || MAX_DEPTH;
32 766   100     1715 my $preserve = delete $args{preserve} || 0;
33 766 100       1463 if ($preserve == 1) {
34 1         2 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
35             }
36 766   50     1393 my $cyclic_refs = delete $args{cyclic_refs} || 'fatal';
37             die "Invalid value for cyclic_refs: $cyclic_refs"
38 766 100       1812 unless $cyclic_refs{ $cyclic_refs };
39 765         1025 my $schemas = delete $args{schemas};
40              
41 765 50       1381 if (keys %args) {
42 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
43             }
44              
45 765         3166 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 765         1780 $self->init;
55 765         2202 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         29 return bless $clone, ref $self;
69             }
70              
71             sub init {
72 2976     2976 1 4511 my ($self) = @_;
73 2976         6767 $self->set_docs([]);
74 2976         6699 $self->set_stack([]);
75 2976         5962 $self->set_anchors({});
76 2976         5578 $self->set_yaml_version($self->default_yaml_version);
77 2976         5298 $self->set_schema($self->schemas->{ $self->yaml_version } );
78             }
79              
80 4473     4473 1 6347 sub docs { return $_[0]->{docs} }
81 39884     39884 1 55755 sub stack { return $_[0]->{stack} }
82 588     588 1 1374 sub anchors { return $_[0]->{anchors} }
83 2976     2976 1 6493 sub set_docs { $_[0]->{docs} = $_[1] }
84 5270     5270 1 12291 sub set_stack { $_[0]->{stack} = $_[1] }
85 5270     5270 1 8435 sub set_anchors { $_[0]->{anchors} = $_[1] }
86 3010     3010 0 5697 sub schemas { return $_[0]->{schemas} }
87 29449     29449 1 76433 sub schema { return $_[0]->{schema} }
88 3010     3010 1 5381 sub set_schema { $_[0]->{schema} = $_[1] }
89 18     18 1 75 sub cyclic_refs { return $_[0]->{cyclic_refs} }
90 0     0 1 0 sub set_cyclic_refs { $_[0]->{cyclic_refs} = $_[1] }
91 2976     2976 0 7076 sub yaml_version { return $_[0]->{yaml_version} }
92 3010     3010 0 4710 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
93 3000     3000 0 7364 sub default_yaml_version { return $_[0]->{default_yaml_version} }
94 1629     1629 0 2740 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
95 26629     26629 0 33757 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
96 2857     2857 0 4187 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
97 29480     29480 0 43710 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
98 5     5 0 14 sub duplicate_keys { return $_[0]->{duplicate_keys} }
99 2137     2137 0 6209 sub require_footer { return $_[0]->{require_footer} }
100 2865     2865 0 9638 sub max_depth { return $_[0]->{max_depth} }
101              
102             sub document_start_event {
103 2329     2329 1 3436 my ($self, $event) = @_;
104 2329         4040 my $stack = $self->stack;
105 2329 100       4172 if ($event->{version_directive}) {
106 34         59 my $version = $event->{version_directive};
107 34         67 $version = "$version->{major}.$version->{minor}";
108 34 100       103 if ($self->{schemas}->{ $version }) {
109 22         49 $self->set_yaml_version($version);
110 22         36 $self->set_schema($self->schemas->{ $version });
111             }
112             else {
113 12         28 $self->set_yaml_version($self->default_yaml_version);
114 12         29 $self->set_schema($self->schemas->{ $self->default_yaml_version });
115             }
116             }
117 2329         2837 my $ref = [];
118 2329         8814 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
119             }
120              
121             sub document_end_event {
122 2297     2297 1 3428 my ($self, $event) = @_;
123 2297         3674 my $stack = $self->stack;
124 2297         3164 my $last = pop @$stack;
125 2297 50       5154 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
126 2297 50       4392 if (@$stack) {
127 0         0 die "Got unexpected end of document";
128             }
129 2297         3833 my $docs = $self->docs;
130 2297 100 100     6115 if ($event->{implicit} and $self->require_footer) {
131 3         36 die sprintf "load: Document (%d) did not end with '...' (require_footer=1)", 1 + scalar @$docs;
132             }
133 2294         4342 push @$docs, $last->{ref}->[0];
134 2294         5164 $self->set_anchors({});
135 2294         3962 $self->set_stack([]);
136             }
137              
138             sub _check_depth {
139 2861     2861   3920 my ($self) = @_;
140 2861         4343 my $stack = $self->stack;
141 2861         3521 my $c = @$stack;
142 2861 100 50     4418 if ($c > ($self->max_depth || MAX_DEPTH)){
143 4         7 croak sprintf 'Depth of nesting exceeds maximum %s', $self->max_depth;
144             }
145             }
146              
147             sub mapping_start_event {
148 1631     1631 1 2559 my ($self, $event) = @_;
149 1631         3066 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
150 1631         6383 my $ref = {
151             type => 'mapping',
152             ref => [],
153             data => \$data,
154             event => $event,
155             on_data => $on_data,
156             };
157 1631         3611 $self->_check_depth;
158 1629         2405 my $stack = $self->stack;
159              
160 1629         2919 my $preserve_order = $self->preserve_order;
161 1629         2667 my $preserve_style = $self->preserve_flow_style;
162 1629         2420 my $preserve_alias = $self->preserve_alias;
163 1629 100 100     7056 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
164 24         133 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
165             }
166 1629 100       2631 if ($preserve_style) {
167 15         21 my $t = tied %$data;
168 15         29 $t->{style} = $event->{style};
169             }
170              
171 1629         2324 push @$stack, $ref;
172 1629 100       5130 if (defined(my $anchor = $event->{anchor})) {
173 77 100       123 if ($preserve_alias) {
174 6         10 my $t = tied %$data;
175 6 100       11 unless (exists $self->anchors->{ $anchor }) {
176             # Repeated anchors cannot be preserved
177 5         10 $t->{alias} = $anchor;
178             }
179             }
180 77         218 $self->anchors->{ $anchor } = { data => $ref->{data} };
181             }
182             }
183              
184             sub mapping_end_event {
185 1607     1607 1 2279 my ($self, $event) = @_;
186 1607         2591 my $stack = $self->stack;
187              
188 1607         2202 my $last = pop @$stack;
189 1607         2260 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1607         3152  
190 1607 50       3301 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
191              
192 1607         2169 my @merge_keys;
193             my @ref;
194 1607         2992 for (my $i = 0; $i < @$ref; $i += 2) {
195 12136         13920 my $key = $ref->[ $i ];
196 12136 100       13298 if (ref $key eq 'YAML::PP::Type::MergeKey') {
197 6         10 my $merge = $ref->[ $i + 1 ];
198 6 100 100     25 if ((reftype($merge) || '') eq 'HASH') {
    100 100        
199 1         3 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         23 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 12130         24479 push @ref, $key, $ref->[ $i + 1 ];
217             }
218             }
219 1604         2621 for my $merge (@merge_keys) {
220 6         13 for my $key (keys %$merge) {
221 9 100       15 unless (exists $$data->{ $key }) {
222 8         40 $$data->{ $key } = $merge->{ $key };
223             }
224             }
225             }
226             my $on_data = $last->{on_data} || sub {
227 1534     1534   2389 my ($self, $hash, $items) = @_;
228 1534         1769 my %seen;
229 1534         3156 for (my $i = 0; $i < @$items; $i += 2) {
230 12010         15495 my ($key, $value) = @$items[ $i, $i + 1 ];
231 12010         13807 $key = '' unless defined $key;
232 12010         13770 if (ref $key) {
233 71         115 $key = $self->stringify_complex($key);
234             }
235 12010         24508 if ($seen{ $key }++ and not $self->duplicate_keys) {
236 2         310 croak "Duplicate key '$key'";
237             }
238 12008         22755 $$hash->{ $key } = $value;
239             }
240 1604   66     10795 };
241 1604         3912 $on_data->($self, $data, \@ref);
242 1592         2041 push @{ $stack->[-1]->{ref} }, $$data;
  1592         3548  
243 1592 100       3590 if (defined(my $anchor = $last->{event}->{anchor})) {
244 73         138 $self->anchors->{ $anchor }->{finished} = 1;
245             }
246 1592         15161 return;
247             }
248              
249             sub sequence_start_event {
250 1230     1230 1 1764 my ($self, $event) = @_;
251 1230         2178 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
252 1230         4663 my $ref = {
253             type => 'sequence',
254             ref => [],
255             data => \$data,
256             event => $event,
257             on_data => $on_data,
258             };
259 1230         2038 my $stack = $self->stack;
260 1230         2609 $self->_check_depth;
261              
262 1228         1902 my $preserve_style = $self->preserve_flow_style;
263 1228         1939 my $preserve_alias = $self->preserve_alias;
264 1228 100 66     3675 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
265 9         50 tie @$data, 'YAML::PP::Preserve::Array', @$data;
266 9         15 my $t = tied @$data;
267 9         29 $t->{style} = $event->{style};
268             }
269              
270 1228         1582 push @$stack, $ref;
271 1228 100       3884 if (defined(my $anchor = $event->{anchor})) {
272 30 100       74 if ($preserve_alias) {
273 6         11 my $t = tied @$data;
274 6 100       10 unless (exists $self->anchors->{ $anchor }) {
275             # Repeated anchors cannot be preserved
276 5         9 $t->{alias} = $anchor;
277             }
278             }
279 30         69 $self->anchors->{ $anchor } = { data => $ref->{data} };
280             }
281             }
282              
283             sub sequence_end_event {
284 1210     1210 1 1732 my ($self, $event) = @_;
285 1210         1796 my $stack = $self->stack;
286 1210         1654 my $last = pop @$stack;
287 1210 50       2686 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
288 1210         1546 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1210         2309  
289              
290             my $on_data = $last->{on_data} || sub {
291 1195     1195   1636 my ($self, $array, $items) = @_;
292 1195         2704 push @$$array, @$items;
293 1210   66     6626 };
294 1210         2510 $on_data->($self, $data, $ref);
295 1210         1443 push @{ $stack->[-1]->{ref} }, $$data;
  1210         2645  
296 1210 100       2730 if (defined(my $anchor = $last->{event}->{anchor})) {
297 30         62 my $test = $self->anchors->{ $anchor };
298 30         82 $self->anchors->{ $anchor }->{finished} = 1;
299             }
300 1210         6210 return;
301             }
302              
303       2211 1   sub stream_start_event {}
304              
305       2176 1   sub stream_end_event {}
306              
307             sub scalar_event {
308 26588     26588 1 33330 my ($self, $event) = @_;
309 26588         25424 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
310 26588         39868 my $value = $self->schema->load_scalar($self, $event);
311 26579         46987 my $last = $self->stack->[-1];
312 26579         39720 my $preserve_alias = $self->preserve_alias;
313 26579         39139 my $preserve_style = $self->preserve_scalar_style;
314 26579 100 100     74588 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
315             my %args = (
316             value => $value,
317             tag => $event->{tag},
318 83         204 );
319 83 100       131 if ($preserve_style) {
320 17         26 $args{style} = $event->{style};
321             }
322 83 100 100     194 if ($preserve_alias and defined $event->{anchor}) {
323 6         9 my $anchor = $event->{anchor};
324 6 100       12 unless (exists $self->anchors->{ $anchor }) {
325             # Repeated anchors cannot be preserved
326 5         11 $args{alias} = $event->{anchor};
327             }
328             }
329 83         262 $value = YAML::PP::Preserve::Scalar->new( %args );
330             }
331 26579 100       45815 if (defined (my $name = $event->{anchor})) {
332 185         549 $self->anchors->{ $name } = { data => \$value, finished => 1 };
333             }
334 26579         26765 push @{ $last->{ref} }, $value;
  26579         65476  
335             }
336              
337             sub alias_event {
338 145     145 1 293 my ($self, $event) = @_;
339 145         240 my $value;
340 145         210 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       308 unless ($anchor->{finished} ) {
345 9         21 my $cyclic_refs = $self->cyclic_refs;
346 9 100       23 if ($cyclic_refs ne 'allow') {
347 4 100       6 if ($cyclic_refs eq 'fatal') {
348 2         323 croak "Found cyclic ref for alias '$name'";
349             }
350 2 100       11 if ($cyclic_refs eq 'warn') {
    50          
351 1         3 $anchor = { data => \undef };
352 1         16 warn "Found cyclic ref for alias '$name'";
353             }
354             elsif ($cyclic_refs eq 'ignore') {
355 1         4 $anchor = { data => \undef };
356             }
357             }
358             }
359 142         192 $value = $anchor->{data};
360             }
361             else {
362 1         151 croak "No anchor defined for alias '$name'";
363             }
364 142         251 my $last = $self->stack->[-1];
365 142         166 push @{ $last->{ref} }, $$value;
  142         335  
366             }
367              
368             sub stringify_complex {
369 75     75 1 102 my ($self, $data) = @_;
370 75 50 66     166 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         153 require Data::Dumper;
375 25         41 local $Data::Dumper::Quotekeys = 0;
376 25         37 local $Data::Dumper::Terse = 1;
377 25         122 local $Data::Dumper::Indent = 0;
378 25         60 local $Data::Dumper::Useqq = 0;
379 25         36 local $Data::Dumper::Sortkeys = 1;
380 25         128 my $string = Data::Dumper->Dump([$data], ['data']);
381 25         1297 $string =~ s/^\$data = //;
382 25         65 return $string;
383             }
384              
385             1;
386              
387             __END__