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   176241 use strict;
  48         81  
  48         1428  
3 48     48   162 use warnings;
  48         62  
  48         2831  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = 'v0.40.1'; # TRIAL VERSION
7              
8 48     48   1979 use YAML::PP;
  48         63  
  48         1411  
9 48         2656 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 48     48   178 /;
  48         68  
12 48     48   273 use Scalar::Util qw/ reftype /;
  48         59  
  48         1927  
13 48     48   214 use Carp qw/ croak /;
  48         72  
  48         2804  
14              
15 48 50 33 48   218 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  48         61  
  48         4057  
16 48 50   48   220 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  48         65  
  48         2280  
17              
18 48     48   209 use constant MAX_DEPTH => 2 ** 9;
  48         63  
  48         115008  
19              
20             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
21              
22             sub new {
23 766     766 1 2414 my ($class, %args) = @_;
24              
25 766         1106 my $default_yaml_version = delete $args{default_yaml_version};
26 766         1072 my $duplicate_keys = delete $args{duplicate_keys};
27 766 100       1397 unless (defined $duplicate_keys) {
28 453         548 $duplicate_keys = 0;
29             }
30 766         888 my $require_footer = delete $args{require_footer};
31 766   100     1918 my $max_depth = delete $args{max_depth} || MAX_DEPTH;
32 766   100     1940 my $preserve = delete $args{preserve} || 0;
33 766 100       1380 if ($preserve == 1) {
34 1         2 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
35             }
36 766   50     1310 my $cyclic_refs = delete $args{cyclic_refs} || 'fatal';
37             die "Invalid value for cyclic_refs: $cyclic_refs"
38 766 100       1809 unless $cyclic_refs{ $cyclic_refs };
39 765         895 my $schemas = delete $args{schemas};
40              
41 765 50       2602 if (keys %args) {
42 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
43             }
44              
45 765         3834 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         1848 $self->init;
55 765         2154 return $self;
56             }
57              
58             sub clone {
59 9     9 0 15 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         37 return bless $clone, ref $self;
69             }
70              
71             sub init {
72 2976     2976 1 4636 my ($self) = @_;
73 2976         6597 $self->set_docs([]);
74 2976         6606 $self->set_stack([]);
75 2976         6088 $self->set_anchors({});
76 2976         5387 $self->set_yaml_version($self->default_yaml_version);
77 2976         5217 $self->set_schema($self->schemas->{ $self->yaml_version } );
78             }
79              
80 4473     4473 1 6234 sub docs { return $_[0]->{docs} }
81 39884     39884 1 56232 sub stack { return $_[0]->{stack} }
82 588     588 1 1559 sub anchors { return $_[0]->{anchors} }
83 2976     2976 1 6018 sub set_docs { $_[0]->{docs} = $_[1] }
84 5270     5270 1 12427 sub set_stack { $_[0]->{stack} = $_[1] }
85 5270     5270 1 8561 sub set_anchors { $_[0]->{anchors} = $_[1] }
86 3010     3010 0 5916 sub schemas { return $_[0]->{schemas} }
87 29449     29449 1 74355 sub schema { return $_[0]->{schema} }
88 3010     3010 1 5351 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 6798 sub yaml_version { return $_[0]->{yaml_version} }
92 3010     3010 0 4838 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
93 3000     3000 0 7638 sub default_yaml_version { return $_[0]->{default_yaml_version} }
94 1629     1629 0 2890 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
95 26629     26629 0 33880 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
96 2857     2857 0 4156 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
97 29480     29480 0 44580 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
98 5     5 0 14 sub duplicate_keys { return $_[0]->{duplicate_keys} }
99 2137     2137 0 5853 sub require_footer { return $_[0]->{require_footer} }
100 2865     2865 0 9917 sub max_depth { return $_[0]->{max_depth} }
101              
102             sub document_start_event {
103 2329     2329 1 3251 my ($self, $event) = @_;
104 2329         4453 my $stack = $self->stack;
105 2329 100       4043 if ($event->{version_directive}) {
106 34         58 my $version = $event->{version_directive};
107 34         68 $version = "$version->{major}.$version->{minor}";
108 34 100       80 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         23 $self->set_yaml_version($self->default_yaml_version);
114 12         22 $self->set_schema($self->schemas->{ $self->default_yaml_version });
115             }
116             }
117 2329         2965 my $ref = [];
118 2329         8759 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
119             }
120              
121             sub document_end_event {
122 2297     2297 1 3307 my ($self, $event) = @_;
123 2297         3789 my $stack = $self->stack;
124 2297         3244 my $last = pop @$stack;
125 2297 50       5003 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
126 2297 50       4136 if (@$stack) {
127 0         0 die "Got unexpected end of document";
128             }
129 2297         4073 my $docs = $self->docs;
130 2297 100 100     6130 if ($event->{implicit} and $self->require_footer) {
131 3         42 die sprintf "load: Document (%d) did not end with '...' (require_footer=1)", 1 + scalar @$docs;
132             }
133 2294         4539 push @$docs, $last->{ref}->[0];
134 2294         4819 $self->set_anchors({});
135 2294         4032 $self->set_stack([]);
136             }
137              
138             sub _check_depth {
139 2861     2861   3788 my ($self) = @_;
140 2861         4390 my $stack = $self->stack;
141 2861         3543 my $c = @$stack;
142 2861 100 50     4398 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 2325 my ($self, $event) = @_;
149 1631         3035 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
150 1631         6502 my $ref = {
151             type => 'mapping',
152             ref => [],
153             data => \$data,
154             event => $event,
155             on_data => $on_data,
156             };
157 1631         3644 $self->_check_depth;
158 1629         2391 my $stack = $self->stack;
159              
160 1629         2938 my $preserve_order = $self->preserve_order;
161 1629         2558 my $preserve_style = $self->preserve_flow_style;
162 1629         2321 my $preserve_alias = $self->preserve_alias;
163 1629 100 100     6633 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
164 24         140 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
165             }
166 1629 100       2498 if ($preserve_style) {
167 15         22 my $t = tied %$data;
168 15         33 $t->{style} = $event->{style};
169             }
170              
171 1629         2053 push @$stack, $ref;
172 1629 100       5252 if (defined(my $anchor = $event->{anchor})) {
173 77 100       146 if ($preserve_alias) {
174 6         12 my $t = tied %$data;
175 6 100       12 unless (exists $self->anchors->{ $anchor }) {
176             # Repeated anchors cannot be preserved
177 5         14 $t->{alias} = $anchor;
178             }
179             }
180 77         185 $self->anchors->{ $anchor } = { data => $ref->{data} };
181             }
182             }
183              
184             sub mapping_end_event {
185 1607     1607 1 2230 my ($self, $event) = @_;
186 1607         2527 my $stack = $self->stack;
187              
188 1607         2200 my $last = pop @$stack;
189 1607         2104 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1607         3121  
190 1607 50       3397 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
191              
192 1607         2227 my @merge_keys;
193             my @ref;
194 1607         3189 for (my $i = 0; $i < @$ref; $i += 2) {
195 12136         14264 my $key = $ref->[ $i ];
196 12136 100       13662 if (ref $key eq 'YAML::PP::Type::MergeKey') {
197 6         8 my $merge = $ref->[ $i + 1 ];
198 6 100 100     26 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     14 if ((reftype($item) || '') eq 'HASH') {
204 5         8 push @merge_keys, $item;
205             }
206             else {
207 2         22 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         25004 push @ref, $key, $ref->[ $i + 1 ];
217             }
218             }
219 1604         2645 for my $merge (@merge_keys) {
220 6         10 for my $key (keys %$merge) {
221 9 100       16 unless (exists $$data->{ $key }) {
222 8         14 $$data->{ $key } = $merge->{ $key };
223             }
224             }
225             }
226             my $on_data = $last->{on_data} || sub {
227 1534     1534   2320 my ($self, $hash, $items) = @_;
228 1534         1995 my %seen;
229 1534         2948 for (my $i = 0; $i < @$items; $i += 2) {
230 12010         15754 my ($key, $value) = @$items[ $i, $i + 1 ];
231 12010         14818 $key = '' unless defined $key;
232 12010         13909 if (ref $key) {
233 71         117 $key = $self->stringify_complex($key);
234             }
235 12010         25309 if ($seen{ $key }++ and not $self->duplicate_keys) {
236 2         270 croak "Duplicate key '$key'";
237             }
238 12008         23711 $$hash->{ $key } = $value;
239             }
240 1604   66     10381 };
241 1604         4125 $on_data->($self, $data, \@ref);
242 1592         1996 push @{ $stack->[-1]->{ref} }, $$data;
  1592         3696  
243 1592 100       3518 if (defined(my $anchor = $last->{event}->{anchor})) {
244 73         161 $self->anchors->{ $anchor }->{finished} = 1;
245             }
246 1592         15295 return;
247             }
248              
249             sub sequence_start_event {
250 1230     1230 1 1747 my ($self, $event) = @_;
251 1230         2364 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
252 1230         4655 my $ref = {
253             type => 'sequence',
254             ref => [],
255             data => \$data,
256             event => $event,
257             on_data => $on_data,
258             };
259 1230         2095 my $stack = $self->stack;
260 1230         2659 $self->_check_depth;
261              
262 1228         2250 my $preserve_style = $self->preserve_flow_style;
263 1228         1896 my $preserve_alias = $self->preserve_alias;
264 1228 100 66     3531 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
265 9         52 tie @$data, 'YAML::PP::Preserve::Array', @$data;
266 9         17 my $t = tied @$data;
267 9         30 $t->{style} = $event->{style};
268             }
269              
270 1228         1631 push @$stack, $ref;
271 1228 100       3828 if (defined(my $anchor = $event->{anchor})) {
272 30 100       84 if ($preserve_alias) {
273 6         11 my $t = tied @$data;
274 6 100       12 unless (exists $self->anchors->{ $anchor }) {
275             # Repeated anchors cannot be preserved
276 5         14 $t->{alias} = $anchor;
277             }
278             }
279 30         92 $self->anchors->{ $anchor } = { data => $ref->{data} };
280             }
281             }
282              
283             sub sequence_end_event {
284 1210     1210 1 1603 my ($self, $event) = @_;
285 1210         1827 my $stack = $self->stack;
286 1210         1634 my $last = pop @$stack;
287 1210 50       2708 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
288 1210         1581 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1210         2240  
289              
290             my $on_data = $last->{on_data} || sub {
291 1195     1195   1879 my ($self, $array, $items) = @_;
292 1195         2733 push @$$array, @$items;
293 1210   66     6813 };
294 1210         2694 $on_data->($self, $data, $ref);
295 1210         1303 push @{ $stack->[-1]->{ref} }, $$data;
  1210         2332  
296 1210 100       2674 if (defined(my $anchor = $last->{event}->{anchor})) {
297 30         116 my $test = $self->anchors->{ $anchor };
298 30         57 $self->anchors->{ $anchor }->{finished} = 1;
299             }
300 1210         6009 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 33988 my ($self, $event) = @_;
309 26588         25453 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
310 26588         41778 my $value = $self->schema->load_scalar($self, $event);
311 26579         52160 my $last = $self->stack->[-1];
312 26579         38764 my $preserve_alias = $self->preserve_alias;
313 26579         38374 my $preserve_style = $self->preserve_scalar_style;
314 26579 100 100     75274 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
315             my %args = (
316             value => $value,
317             tag => $event->{tag},
318 83         232 );
319 83 100       138 if ($preserve_style) {
320 17         35 $args{style} = $event->{style};
321             }
322 83 100 100     224 if ($preserve_alias and defined $event->{anchor}) {
323 6         10 my $anchor = $event->{anchor};
324 6 100       13 unless (exists $self->anchors->{ $anchor }) {
325             # Repeated anchors cannot be preserved
326 5         12 $args{alias} = $event->{anchor};
327             }
328             }
329 83         285 $value = YAML::PP::Preserve::Scalar->new( %args );
330             }
331 26579 100       45357 if (defined (my $name = $event->{anchor})) {
332 185         577 $self->anchors->{ $name } = { data => \$value, finished => 1 };
333             }
334 26579         28039 push @{ $last->{ref} }, $value;
  26579         66909  
335             }
336              
337             sub alias_event {
338 145     145 1 246 my ($self, $event) = @_;
339 145         188 my $value;
340 145         358 my $name = $event->{value};
341 145 100       381 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       350 unless ($anchor->{finished} ) {
345 9         24 my $cyclic_refs = $self->cyclic_refs;
346 9 100       35 if ($cyclic_refs ne 'allow') {
347 4 100       6 if ($cyclic_refs eq 'fatal') {
348 2         52 croak "Found cyclic ref for alias '$name'";
349             }
350 2 100       6 if ($cyclic_refs eq 'warn') {
    50          
351 1         3 $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         307 $value = $anchor->{data};
360             }
361             else {
362 1         172 croak "No anchor defined for alias '$name'";
363             }
364 142         241 my $last = $self->stack->[-1];
365 142         181 push @{ $last->{ref} }, $$value;
  142         422  
366             }
367              
368             sub stringify_complex {
369 75     75 1 134 my ($self, $data) = @_;
370 75 50 66     148 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         128 require Data::Dumper;
375 25         33 local $Data::Dumper::Quotekeys = 0;
376 25         37 local $Data::Dumper::Terse = 1;
377 25         59 local $Data::Dumper::Indent = 0;
378 25         59 local $Data::Dumper::Useqq = 0;
379 25         50 local $Data::Dumper::Sortkeys = 1;
380 25         204 my $string = Data::Dumper->Dump([$data], ['data']);
381 25         1279 $string =~ s/^\$data = //;
382 25         75 return $string;
383             }
384              
385             1;
386              
387             __END__