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   148089 use strict;
  48         81  
  48         1486  
3 48     48   174 use warnings;
  48         65  
  48         2994  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = 'v0.41.1'; # TRIAL VERSION
7              
8 48     48   1965 use YAML::PP;
  48         68  
  48         1400  
9 48         2712 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 48     48   194 /;
  48         84  
12 48     48   229 use Scalar::Util qw/ reftype /;
  48         99  
  48         1925  
13 48     48   181 use Carp qw/ croak /;
  48         80  
  48         3011  
14              
15 48 50 33 48   254 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  48         80  
  48         3943  
16 48 50   48   204 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  48         84  
  48         2576  
17              
18 48     48   211 use constant MAX_DEPTH => 2 ** 9;
  48         79  
  48         119068  
19              
20             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
21              
22             sub new {
23 766     766 1 2423 my ($class, %args) = @_;
24              
25 766         1151 my $default_yaml_version = delete $args{default_yaml_version};
26 766         1065 my $duplicate_keys = delete $args{duplicate_keys};
27 766 100       1498 unless (defined $duplicate_keys) {
28 453         601 $duplicate_keys = 0;
29             }
30 766         952 my $require_footer = delete $args{require_footer};
31 766   100     2126 my $max_depth = delete $args{max_depth} || MAX_DEPTH;
32 766   100     1883 my $preserve = delete $args{preserve} || 0;
33 766 100       1512 if ($preserve == 1) {
34 1         2 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
35             }
36 766   50     1558 my $cyclic_refs = delete $args{cyclic_refs} || 'fatal';
37             die "Invalid value for cyclic_refs: $cyclic_refs"
38 766 100       1907 unless $cyclic_refs{ $cyclic_refs };
39 765         1021 my $schemas = delete $args{schemas};
40              
41 765 50       1538 if (keys %args) {
42 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
43             }
44              
45 765         3657 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         1880 $self->init;
55 765         2203 return $self;
56             }
57              
58             sub clone {
59 9     9 0 13 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         27 };
68 9         29 return bless $clone, ref $self;
69             }
70              
71             sub init {
72 2976     2976 1 4598 my ($self) = @_;
73 2976         6578 $self->set_docs([]);
74 2976         6084 $self->set_stack([]);
75 2976         6452 $self->set_anchors({});
76 2976         5842 $self->set_yaml_version($self->default_yaml_version);
77 2976         5095 $self->set_schema($self->schemas->{ $self->yaml_version } );
78             }
79              
80 4473     4473 1 6141 sub docs { return $_[0]->{docs} }
81 39884     39884 1 54100 sub stack { return $_[0]->{stack} }
82 588     588 1 1449 sub anchors { return $_[0]->{anchors} }
83 2976     2976 1 5661 sub set_docs { $_[0]->{docs} = $_[1] }
84 5270     5270 1 12206 sub set_stack { $_[0]->{stack} = $_[1] }
85 5270     5270 1 8674 sub set_anchors { $_[0]->{anchors} = $_[1] }
86 3010     3010 0 5856 sub schemas { return $_[0]->{schemas} }
87 29449     29449 1 72140 sub schema { return $_[0]->{schema} }
88 3010     3010 1 5022 sub set_schema { $_[0]->{schema} = $_[1] }
89 18     18 1 64 sub cyclic_refs { return $_[0]->{cyclic_refs} }
90 0     0 1 0 sub set_cyclic_refs { $_[0]->{cyclic_refs} = $_[1] }
91 2976     2976 0 6508 sub yaml_version { return $_[0]->{yaml_version} }
92 3010     3010 0 4783 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
93 3000     3000 0 7260 sub default_yaml_version { return $_[0]->{default_yaml_version} }
94 1629     1629 0 2746 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
95 26629     26629 0 34357 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
96 2857     2857 0 4273 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
97 29480     29480 0 43735 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
98 5     5 0 14 sub duplicate_keys { return $_[0]->{duplicate_keys} }
99 2137     2137 0 5551 sub require_footer { return $_[0]->{require_footer} }
100 2865     2865 0 9928 sub max_depth { return $_[0]->{max_depth} }
101              
102             sub document_start_event {
103 2329     2329 1 3274 my ($self, $event) = @_;
104 2329         4133 my $stack = $self->stack;
105 2329 100       4067 if ($event->{version_directive}) {
106 34         55 my $version = $event->{version_directive};
107 34         69 $version = "$version->{major}.$version->{minor}";
108 34 100       100 if ($self->{schemas}->{ $version }) {
109 22         52 $self->set_yaml_version($version);
110 22         42 $self->set_schema($self->schemas->{ $version });
111             }
112             else {
113 12         30 $self->set_yaml_version($self->default_yaml_version);
114 12         29 $self->set_schema($self->schemas->{ $self->default_yaml_version });
115             }
116             }
117 2329         3028 my $ref = [];
118 2329         8844 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
119             }
120              
121             sub document_end_event {
122 2297     2297 1 3479 my ($self, $event) = @_;
123 2297         3726 my $stack = $self->stack;
124 2297         3113 my $last = pop @$stack;
125 2297 50       4967 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
126 2297 50       4243 if (@$stack) {
127 0         0 die "Got unexpected end of document";
128             }
129 2297         3890 my $docs = $self->docs;
130 2297 100 100     6581 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         4510 push @$docs, $last->{ref}->[0];
134 2294         5298 $self->set_anchors({});
135 2294         3963 $self->set_stack([]);
136             }
137              
138             sub _check_depth {
139 2861     2861   3913 my ($self) = @_;
140 2861         4252 my $stack = $self->stack;
141 2861         3636 my $c = @$stack;
142 2861 100 50     4848 if ($c > ($self->max_depth || MAX_DEPTH)){
143 4         5 croak sprintf 'Depth of nesting exceeds maximum %s', $self->max_depth;
144             }
145             }
146              
147             sub mapping_start_event {
148 1631     1631 1 2467 my ($self, $event) = @_;
149 1631         2857 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
150 1631         6592 my $ref = {
151             type => 'mapping',
152             ref => [],
153             data => \$data,
154             event => $event,
155             on_data => $on_data,
156             };
157 1631         3686 $self->_check_depth;
158 1629         2481 my $stack = $self->stack;
159              
160 1629         2892 my $preserve_order = $self->preserve_order;
161 1629         2847 my $preserve_style = $self->preserve_flow_style;
162 1629         2563 my $preserve_alias = $self->preserve_alias;
163 1629 100 100     6836 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
164 24         188 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
165             }
166 1629 100       2691 if ($preserve_style) {
167 15         29 my $t = tied %$data;
168 15         37 $t->{style} = $event->{style};
169             }
170              
171 1629         2115 push @$stack, $ref;
172 1629 100       4998 if (defined(my $anchor = $event->{anchor})) {
173 77 100       144 if ($preserve_alias) {
174 6         12 my $t = tied %$data;
175 6 100       18 unless (exists $self->anchors->{ $anchor }) {
176             # Repeated anchors cannot be preserved
177 5         15 $t->{alias} = $anchor;
178             }
179             }
180 77         202 $self->anchors->{ $anchor } = { data => $ref->{data} };
181             }
182             }
183              
184             sub mapping_end_event {
185 1607     1607 1 2555 my ($self, $event) = @_;
186 1607         2545 my $stack = $self->stack;
187              
188 1607         2207 my $last = pop @$stack;
189 1607         2079 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1607         3443  
190 1607 50       3479 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
191              
192 1607         2357 my @merge_keys;
193             my @ref;
194 1607         3141 for (my $i = 0; $i < @$ref; $i += 2) {
195 12136         13910 my $key = $ref->[ $i ];
196 12136 100       13448 if (ref $key eq 'YAML::PP::Type::MergeKey') {
197 6         11 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     17 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         12 die "Expected hash or array for merge key";
213             }
214             }
215             else {
216 12130         24558 push @ref, $key, $ref->[ $i + 1 ];
217             }
218             }
219 1604         2697 for my $merge (@merge_keys) {
220 6         12 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 1534     1534   2343 my ($self, $hash, $items) = @_;
228 1534         1966 my %seen;
229 1534         3059 for (my $i = 0; $i < @$items; $i += 2) {
230 12010         16053 my ($key, $value) = @$items[ $i, $i + 1 ];
231 12010         14218 $key = '' unless defined $key;
232 12010         13947 if (ref $key) {
233 71         144 $key = $self->stringify_complex($key);
234             }
235 12010         26929 if ($seen{ $key }++ and not $self->duplicate_keys) {
236 2         262 croak "Duplicate key '$key'";
237             }
238 12008         23628 $$hash->{ $key } = $value;
239             }
240 1604   66     10538 };
241 1604         3862 $on_data->($self, $data, \@ref);
242 1592         2305 push @{ $stack->[-1]->{ref} }, $$data;
  1592         3553  
243 1592 100       3571 if (defined(my $anchor = $last->{event}->{anchor})) {
244 73         135 $self->anchors->{ $anchor }->{finished} = 1;
245             }
246 1592         15436 return;
247             }
248              
249             sub sequence_start_event {
250 1230     1230 1 1836 my ($self, $event) = @_;
251 1230         2062 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
252 1230         4374 my $ref = {
253             type => 'sequence',
254             ref => [],
255             data => \$data,
256             event => $event,
257             on_data => $on_data,
258             };
259 1230         2124 my $stack = $self->stack;
260 1230         2490 $self->_check_depth;
261              
262 1228         1982 my $preserve_style = $self->preserve_flow_style;
263 1228         2036 my $preserve_alias = $self->preserve_alias;
264 1228 100 66     3495 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
265 9         71 tie @$data, 'YAML::PP::Preserve::Array', @$data;
266 9         41 my $t = tied @$data;
267 9         48 $t->{style} = $event->{style};
268             }
269              
270 1228         1699 push @$stack, $ref;
271 1228 100       3889 if (defined(my $anchor = $event->{anchor})) {
272 30 100       78 if ($preserve_alias) {
273 6         8 my $t = tied @$data;
274 6 100       17 unless (exists $self->anchors->{ $anchor }) {
275             # Repeated anchors cannot be preserved
276 5         14 $t->{alias} = $anchor;
277             }
278             }
279 30         115 $self->anchors->{ $anchor } = { data => $ref->{data} };
280             }
281             }
282              
283             sub sequence_end_event {
284 1210     1210 1 1619 my ($self, $event) = @_;
285 1210         1810 my $stack = $self->stack;
286 1210         1575 my $last = pop @$stack;
287 1210 50       2525 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
288 1210         1468 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1210         2256  
289              
290             my $on_data = $last->{on_data} || sub {
291 1195     1195   1734 my ($self, $array, $items) = @_;
292 1195         2648 push @$$array, @$items;
293 1210   66     6624 };
294 1210         2461 $on_data->($self, $data, $ref);
295 1210         1211 push @{ $stack->[-1]->{ref} }, $$data;
  1210         2323  
296 1210 100       2463 if (defined(my $anchor = $last->{event}->{anchor})) {
297 30         65 my $test = $self->anchors->{ $anchor };
298 30         58 $self->anchors->{ $anchor }->{finished} = 1;
299             }
300 1210         6054 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 33484 my ($self, $event) = @_;
309 26588         24852 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
310 26588         39955 my $value = $self->schema->load_scalar($self, $event);
311 26579         46042 my $last = $self->stack->[-1];
312 26579         38534 my $preserve_alias = $self->preserve_alias;
313 26579         38133 my $preserve_style = $self->preserve_scalar_style;
314 26579 100 100     73845 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
315             my %args = (
316             value => $value,
317             tag => $event->{tag},
318 83         277 );
319 83 100       146 if ($preserve_style) {
320 17         33 $args{style} = $event->{style};
321             }
322 83 100 100     266 if ($preserve_alias and defined $event->{anchor}) {
323 6         9 my $anchor = $event->{anchor};
324 6 100       15 unless (exists $self->anchors->{ $anchor }) {
325             # Repeated anchors cannot be preserved
326 5         13 $args{alias} = $event->{anchor};
327             }
328             }
329 83         300 $value = YAML::PP::Preserve::Scalar->new( %args );
330             }
331 26579 100       44497 if (defined (my $name = $event->{anchor})) {
332 185         559 $self->anchors->{ $name } = { data => \$value, finished => 1 };
333             }
334 26579         26892 push @{ $last->{ref} }, $value;
  26579         63923  
335             }
336              
337             sub alias_event {
338 145     145 1 224 my ($self, $event) = @_;
339 145         165 my $value;
340 145         204 my $name = $event->{value};
341 145 100       234 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       319 unless ($anchor->{finished} ) {
345 9         20 my $cyclic_refs = $self->cyclic_refs;
346 9 100       22 if ($cyclic_refs ne 'allow') {
347 4 100       6 if ($cyclic_refs eq 'fatal') {
348 2         353 croak "Found cyclic ref for alias '$name'";
349             }
350 2 100       11 if ($cyclic_refs eq 'warn') {
    50          
351 1         4 $anchor = { data => \undef };
352 1         14 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         196 $value = $anchor->{data};
360             }
361             else {
362 1         160 croak "No anchor defined for alias '$name'";
363             }
364 142         244 my $last = $self->stack->[-1];
365 142         170 push @{ $last->{ref} }, $$value;
  142         363  
366             }
367              
368             sub stringify_complex {
369 75     75 1 117 my ($self, $data) = @_;
370 75 50 66     164 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         139 require Data::Dumper;
375 25         37 local $Data::Dumper::Quotekeys = 0;
376 25         41 local $Data::Dumper::Terse = 1;
377 25         41 local $Data::Dumper::Indent = 0;
378 25         34 local $Data::Dumper::Useqq = 0;
379 25         33 local $Data::Dumper::Sortkeys = 1;
380 25         165 my $string = Data::Dumper->Dump([$data], ['data']);
381 25         1284 $string =~ s/^\$data = //;
382 25         58 return $string;
383             }
384              
385             1;
386              
387             __END__