File Coverage

blib/lib/YAML/PP/Constructor.pm
Criterion Covered Total %
statement 227 230 98.7
branch 71 80 88.7
condition 36 44 81.8
subroutine 42 43 97.6
pod 23 33 69.7
total 399 430 92.7


line stmt bran cond sub pod time code
1             # ABSTRACT: Construct data structure from Parser Events
2 35     35   253 use strict;
  35         77  
  35         1121  
3 35     35   211 use warnings;
  35         88  
  35         1734  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = '0.036'; # VERSION
7              
8 35     35   1264 use YAML::PP;
  35         85  
  35         1087  
9 35         2151 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 35     35   209 /;
  35         84  
12 35     35   260 use Scalar::Util qw/ reftype /;
  35         103  
  35         1779  
13 35     35   231 use Carp qw/ croak /;
  35         86  
  35         2646  
14              
15 35 50 33 35   317 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  35         83  
  35         3438  
16 35 50   35   278 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  35         102  
  35         103447  
17              
18             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
19              
20             sub new {
21 186     186 1 764 my ($class, %args) = @_;
22              
23 186         382 my $default_yaml_version = delete $args{default_yaml_version};
24 186         351 my $duplicate_keys = delete $args{duplicate_keys};
25 186 100       510 unless (defined $duplicate_keys) {
26 166         289 $duplicate_keys = 0;
27             }
28 186   100     675 my $preserve = delete $args{preserve} || 0;
29 186 100       460 if ($preserve == 1) {
30 1         2 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
31             }
32 186   50     488 my $cyclic_refs = delete $args{cyclic_refs} || 'allow';
33             die "Invalid value for cyclic_refs: $cyclic_refs"
34 186 100       594 unless $cyclic_refs{ $cyclic_refs };
35 185         318 my $schemas = delete $args{schemas};
36              
37 185 50       521 if (keys %args) {
38 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
39             }
40              
41 185         851 my $self = bless {
42             default_yaml_version => $default_yaml_version,
43             schemas => $schemas,
44             cyclic_refs => $cyclic_refs,
45             preserve => $preserve,
46             duplicate_keys => $duplicate_keys,
47             }, $class;
48 185         566 $self->init;
49 185         738 return $self;
50             }
51              
52             sub clone {
53 9     9 0 17 my ($self) = @_;
54             my $clone = {
55             schemas => $self->{schemas},
56             schema => $self->{schema},
57             default_yaml_version => $self->{default_yaml_version},
58             cyclic_refs => $self->cyclic_refs,
59             preserve => $self->{preserve},
60 9         25 };
61 9         44 return bless $clone, ref $self;
62             }
63              
64             sub init {
65 1519     1519 1 2590 my ($self) = @_;
66 1519         4008 $self->set_docs([]);
67 1519         4180 $self->set_stack([]);
68 1519         3742 $self->set_anchors({});
69 1519         3270 $self->set_yaml_version($self->default_yaml_version);
70 1519         2949 $self->set_schema($self->schemas->{ $self->yaml_version } );
71             }
72              
73 2656     2656 1 4862 sub docs { return $_[0]->{docs} }
74 9996     9996 1 17791 sub stack { return $_[0]->{stack} }
75 359     359 1 1063 sub anchors { return $_[0]->{anchors} }
76 1519     1519 1 3744 sub set_docs { $_[0]->{docs} = $_[1] }
77 2868     2868 1 7560 sub set_stack { $_[0]->{stack} = $_[1] }
78 2868     2868 1 5425 sub set_anchors { $_[0]->{anchors} = $_[1] }
79 1531     1531 0 3118 sub schemas { return $_[0]->{schemas} }
80 5807     5807 1 18291 sub schema { return $_[0]->{schema} }
81 1531     1531 1 3383 sub set_schema { $_[0]->{schema} = $_[1] }
82 18     18 1 59 sub cyclic_refs { return $_[0]->{cyclic_refs} }
83 0     0 1 0 sub set_cyclic_refs { $_[0]->{cyclic_refs} = $_[1] }
84 1519     1519 0 4093 sub yaml_version { return $_[0]->{yaml_version} }
85 1531     1531 0 2723 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
86 1531     1531 0 4284 sub default_yaml_version { return $_[0]->{default_yaml_version} }
87 588     588 0 1155 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
88 4456     4456 0 7162 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
89 1392     1392 0 2388 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
90 5842     5842 0 10271 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
91 3     3 0 11 sub duplicate_keys { return $_[0]->{duplicate_keys} }
92              
93             sub document_start_event {
94 1376     1376 1 2520 my ($self, $event) = @_;
95 1376         3207 my $stack = $self->stack;
96 1376 100       3407 if ($event->{version_directive}) {
97 12         35 my $version = $event->{version_directive};
98 12         36 $version = "$version->{major}.$version->{minor}";
99 12 100       38 if ($self->{schemas}->{ $version }) {
100 6         17 $self->set_yaml_version($version);
101 6         13 $self->set_schema($self->schemas->{ $version });
102             }
103             else {
104 6         17 $self->set_yaml_version($self->default_yaml_version);
105 6         28 $self->set_schema($self->schemas->{ $self->default_yaml_version });
106             }
107             }
108 1376         2276 my $ref = [];
109 1376         6193 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
110             }
111              
112             sub document_end_event {
113 1349     1349 1 3220 my ($self, $event) = @_;
114 1349         2523 my $stack = $self->stack;
115 1349         3338 my $last = pop @$stack;
116 1349 50       3491 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
117 1349 50       3279 if (@$stack) {
118 0         0 die "Got unexpected end of document";
119             }
120 1349         2522 my $docs = $self->docs;
121 1349         3088 push @$docs, $last->{ref}->[0];
122 1349         3552 $self->set_anchors({});
123 1349         2991 $self->set_stack([]);
124             }
125              
126             sub mapping_start_event {
127 588     588 1 1065 my ($self, $event) = @_;
128 588         1260 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
129 588         2619 my $ref = {
130             type => 'mapping',
131             ref => [],
132             data => \$data,
133             event => $event,
134             on_data => $on_data,
135             };
136 588         1290 my $stack = $self->stack;
137              
138 588         1231 my $preserve_order = $self->preserve_order;
139 588         1153 my $preserve_style = $self->preserve_flow_style;
140 588         1109 my $preserve_alias = $self->preserve_alias;
141 588 100 100     2990 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
142 24         138 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
143             }
144 588 100       1238 if ($preserve_style) {
145 15         27 my $t = tied %$data;
146 15         38 $t->{style} = $event->{style};
147             }
148              
149 588         981 push @$stack, $ref;
150 588 100       2109 if (defined(my $anchor = $event->{anchor})) {
151 49 100       124 if ($preserve_alias) {
152 6         12 my $t = tied %$data;
153 6 100       15 unless (exists $self->anchors->{ $anchor }) {
154             # Repeated anchors cannot be preserved
155 5         12 $t->{alias} = $anchor;
156             }
157             }
158 49         140 $self->anchors->{ $anchor } = { data => $ref->{data} };
159             }
160             }
161              
162             sub mapping_end_event {
163 578     578 1 1069 my ($self, $event) = @_;
164 578         1147 my $stack = $self->stack;
165              
166 578         956 my $last = pop @$stack;
167 578         922 my ($ref, $data) = @{ $last }{qw/ ref data /};
  578         1248  
168 578 50       1424 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
169              
170 578         982 my @merge_keys;
171             my @ref;
172 578         1386 for (my $i = 0; $i < @$ref; $i += 2) {
173 1216         2016 my $key = $ref->[ $i ];
174 1216 100       2044 if (ref $key eq 'YAML::PP::Type::MergeKey') {
175 6         11 my $merge = $ref->[ $i + 1 ];
176 6 100 100     47 if ((reftype($merge) || '') eq 'HASH') {
    100 100        
177 1         4 push @merge_keys, $merge;
178             }
179             elsif ((reftype($merge) || '') eq 'ARRAY') {
180 4         8 for my $item (@$merge) {
181 7 100 100     24 if ((reftype($item) || '') eq 'HASH') {
182 5         14 push @merge_keys, $item;
183             }
184             else {
185 2         25 die "Expected hash for merge key";
186             }
187             }
188             }
189             else {
190 1         14 die "Expected hash or array for merge key";
191             }
192             }
193             else {
194 1210         3339 push @ref, $key, $ref->[ $i + 1 ];
195             }
196             }
197 575         1168 for my $merge (@merge_keys) {
198 6         20 for my $key (keys %$merge) {
199 9 100       20 unless (exists $$data->{ $key }) {
200 8         19 $$data->{ $key } = $merge->{ $key };
201             }
202             }
203             }
204             my $on_data = $last->{on_data} || sub {
205 535     535   1758 my ($self, $hash, $items) = @_;
206 535         735 my %seen;
207 535         1288 for (my $i = 0; $i < @$items; $i += 2) {
208 1146         2483 my ($key, $value) = @$items[ $i, $i + 1 ];
209 1146         2134 $key = '' unless defined $key;
210 1146         2666 if (ref $key) {
211 57         117 $key = $self->stringify_complex($key);
212             }
213 1146         3991 if ($seen{ $key }++ and not $self->duplicate_keys) {
214 2         269 croak "Duplicate key '$key'";
215             }
216 1144         3556 $$hash->{ $key } = $value;
217             }
218 575   100     4155 };
219 575         1762 $on_data->($self, $data, \@ref);
220 563         846 push @{ $stack->[-1]->{ref} }, $$data;
  563         1411  
221 563 100       1436 if (defined(my $anchor = $last->{event}->{anchor})) {
222 47         129 $self->anchors->{ $anchor }->{finished} = 1;
223             }
224 563         4974 return;
225             }
226              
227             sub sequence_start_event {
228 804     804 1 1358 my ($self, $event) = @_;
229 804         1556 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
230 804         3587 my $ref = {
231             type => 'sequence',
232             ref => [],
233             data => \$data,
234             event => $event,
235             on_data => $on_data,
236             };
237 804         1646 my $stack = $self->stack;
238              
239 804         1543 my $preserve_style = $self->preserve_flow_style;
240 804         1364 my $preserve_alias = $self->preserve_alias;
241 804 100 66     2883 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
242 9         48 tie @$data, 'YAML::PP::Preserve::Array', @$data;
243 9         16 my $t = tied @$data;
244 9         36 $t->{style} = $event->{style};
245             }
246              
247 804         1382 push @$stack, $ref;
248 804 100       2979 if (defined(my $anchor = $event->{anchor})) {
249 21 100       81 if ($preserve_alias) {
250 6         12 my $t = tied @$data;
251 6 100       15 unless (exists $self->anchors->{ $anchor }) {
252             # Repeated anchors cannot be preserved
253 5         12 $t->{alias} = $anchor;
254             }
255             }
256 21         76 $self->anchors->{ $anchor } = { data => $ref->{data} };
257             }
258             }
259              
260             sub sequence_end_event {
261 797     797 1 1381 my ($self, $event) = @_;
262 797         1455 my $stack = $self->stack;
263 797         1321 my $last = pop @$stack;
264 797 50       1898 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
265 797         1161 my ($ref, $data) = @{ $last }{qw/ ref data /};
  797         1689  
266              
267             my $on_data = $last->{on_data} || sub {
268 796     796   1354 my ($self, $array, $items) = @_;
269 796         2188 push @$$array, @$items;
270 797   100     5256 };
271 797         2248 $on_data->($self, $data, $ref);
272 797         1062 push @{ $stack->[-1]->{ref} }, $$data;
  797         1676  
273 797 100       2019 if (defined(my $anchor = $last->{event}->{anchor})) {
274 21         49 my $test = $self->anchors->{ $anchor };
275 21         52 $self->anchors->{ $anchor }->{finished} = 1;
276             }
277 797         4334 return;
278             }
279              
280       1334 1   sub stream_start_event {}
281              
282       1307 1   sub stream_end_event {}
283              
284             sub scalar_event {
285 4415     4415 1 7615 my ($self, $event) = @_;
286 4415         6000 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
287 4415         8986 my $value = $self->schema->load_scalar($self, $event);
288 4406         9924 my $last = $self->stack->[-1];
289 4406         9026 my $preserve_alias = $self->preserve_alias;
290 4406         8559 my $preserve_style = $self->preserve_scalar_style;
291 4406 100 100     16997 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
292             my %args = (
293             value => $value,
294             tag => $event->{tag},
295 83         254 );
296 83 100       168 if ($preserve_style) {
297 17         35 $args{style} = $event->{style};
298             }
299 83 100 100     239 if ($preserve_alias and defined $event->{anchor}) {
300 6         18 my $anchor = $event->{anchor};
301 6 100       16 unless (exists $self->anchors->{ $anchor }) {
302             # Repeated anchors cannot be preserved
303 5         11 $args{alias} = $event->{anchor};
304             }
305             }
306 83         328 $value = YAML::PP::Preserve::Scalar->new( %args );
307             }
308 4406 100       10334 if (defined (my $name = $event->{anchor})) {
309 82         359 $self->anchors->{ $name } = { data => \$value, finished => 1 };
310             }
311 4406         5936 push @{ $last->{ref} }, $value;
  4406         14435  
312             }
313              
314             sub alias_event {
315 100     100 1 179 my ($self, $event) = @_;
316 100         156 my $value;
317 100         172 my $name = $event->{value};
318 100 100       201 if (my $anchor = $self->anchors->{ $name }) {
319             # We know this is a cyclic ref since the node hasn't
320             # been constructed completely yet
321 99 100       225 unless ($anchor->{finished} ) {
322 9         32 my $cyclic_refs = $self->cyclic_refs;
323 9 100       29 if ($cyclic_refs ne 'allow') {
324 3 100       15 if ($cyclic_refs eq 'fatal') {
325 1         18 die "Found cyclic ref for alias '$name'";
326             }
327 2 100       9 if ($cyclic_refs eq 'warn') {
    50          
328 1         3 $anchor = { data => \undef };
329 1         15 warn "Found cyclic ref for alias '$name'";
330             }
331             elsif ($cyclic_refs eq 'ignore') {
332 1         4 $anchor = { data => \undef };
333             }
334             }
335             }
336 98         269 $value = $anchor->{data};
337             }
338             else {
339 1         174 croak "No anchor defined for alias '$name'";
340             }
341 98         204 my $last = $self->stack->[-1];
342 98         143 push @{ $last->{ref} }, $$value;
  98         295  
343             }
344              
345             sub stringify_complex {
346 61     61 1 110 my ($self, $data) = @_;
347 61 50 66     180 return $data if (
      66        
348             ref $data eq 'YAML::PP::Preserve::Scalar'
349             and ($self->preserve_scalar_style or $self->preserve_alias)
350             );
351 11         59 require Data::Dumper;
352 11         21 local $Data::Dumper::Quotekeys = 0;
353 11         26 local $Data::Dumper::Terse = 1;
354 11         20 local $Data::Dumper::Indent = 0;
355 11         16 local $Data::Dumper::Useqq = 0;
356 11         17 local $Data::Dumper::Sortkeys = 1;
357 11         77 my $string = Data::Dumper->Dump([$data], ['data']);
358 11         629 $string =~ s/^\$data = //;
359 11         37 return $string;
360             }
361              
362             1;
363              
364             __END__