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   263 use strict;
  35         77  
  35         1448  
3 35     35   180 use warnings;
  35         73  
  35         1691  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = '0.036_002'; # TRIAL VERSION
7              
8 35     35   1195 use YAML::PP;
  35         69  
  35         1554  
9 35         2130 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 35     35   224 /;
  35         72  
12 35     35   255 use Scalar::Util qw/ reftype /;
  35         79  
  35         1860  
13 35     35   233 use Carp qw/ croak /;
  35         110  
  35         2799  
14              
15 35 50 33 35   276 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  35         80  
  35         3347  
16 35 50   35   270 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  35         91  
  35         101160  
17              
18             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
19              
20             sub new {
21 748     748 1 2554 my ($class, %args) = @_;
22              
23 748         1377 my $default_yaml_version = delete $args{default_yaml_version};
24 748         1244 my $duplicate_keys = delete $args{duplicate_keys};
25 748 100       1607 unless (defined $duplicate_keys) {
26 436         730 $duplicate_keys = 0;
27             }
28 748   100     2387 my $preserve = delete $args{preserve} || 0;
29 748 100       1766 if ($preserve == 1) {
30 1         3 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
31             }
32 748   50     1652 my $cyclic_refs = delete $args{cyclic_refs} || 'fatal';
33             die "Invalid value for cyclic_refs: $cyclic_refs"
34 748 100       1926 unless $cyclic_refs{ $cyclic_refs };
35 747         1102 my $schemas = delete $args{schemas};
36              
37 747 50       1817 if (keys %args) {
38 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
39             }
40              
41 747         2855 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 747         2019 $self->init;
49 747         2766 return $self;
50             }
51              
52             sub clone {
53 9     9 0 18 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         35 };
61 9         40 return bless $clone, ref $self;
62             }
63              
64             sub init {
65 2935     2935 1 5092 my ($self) = @_;
66 2935         8132 $self->set_docs([]);
67 2935         7320 $self->set_stack([]);
68 2935         6812 $self->set_anchors({});
69 2935         6495 $self->set_yaml_version($self->default_yaml_version);
70 2935         5564 $self->set_schema($self->schemas->{ $self->yaml_version } );
71             }
72              
73 4437     4437 1 7856 sub docs { return $_[0]->{docs} }
74 17366     17366 1 30269 sub stack { return $_[0]->{stack} }
75 588     588 1 1722 sub anchors { return $_[0]->{anchors} }
76 2935     2935 1 6318 sub set_docs { $_[0]->{docs} = $_[1] }
77 5212     5212 1 13723 sub set_stack { $_[0]->{stack} = $_[1] }
78 5212     5212 1 10763 sub set_anchors { $_[0]->{anchors} = $_[1] }
79 2969     2969 0 6152 sub schemas { return $_[0]->{schemas} }
80 10155     10155 1 32611 sub schema { return $_[0]->{schema} }
81 2969     2969 1 5918 sub set_schema { $_[0]->{schema} = $_[1] }
82 18     18 1 56 sub cyclic_refs { return $_[0]->{cyclic_refs} }
83 0     0 1 0 sub set_cyclic_refs { $_[0]->{cyclic_refs} = $_[1] }
84 2935     2935 0 7208 sub yaml_version { return $_[0]->{yaml_version} }
85 2969     2969 0 5746 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
86 2959     2959 0 7815 sub default_yaml_version { return $_[0]->{default_yaml_version} }
87 1303     1303 0 2482 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
88 7680     7680 0 12154 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
89 2516     2516 0 4326 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
90 10190     10190 0 18752 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
91 5     5 0 26 sub duplicate_keys { return $_[0]->{duplicate_keys} }
92              
93             sub document_start_event {
94 2305     2305 1 4149 my ($self, $event) = @_;
95 2305         4693 my $stack = $self->stack;
96 2305 100       5083 if ($event->{version_directive}) {
97 34         86 my $version = $event->{version_directive};
98 34         109 $version = "$version->{major}.$version->{minor}";
99 34 100       148 if ($self->{schemas}->{ $version }) {
100 22         74 $self->set_yaml_version($version);
101 22         62 $self->set_schema($self->schemas->{ $version });
102             }
103             else {
104 12         36 $self->set_yaml_version($self->default_yaml_version);
105 12         36 $self->set_schema($self->schemas->{ $self->default_yaml_version });
106             }
107             }
108 2305         3974 my $ref = [];
109 2305         10377 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
110             }
111              
112             sub document_end_event {
113 2277     2277 1 4877 my ($self, $event) = @_;
114 2277         4255 my $stack = $self->stack;
115 2277         4562 my $last = pop @$stack;
116 2277 50       5991 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
117 2277 50       4854 if (@$stack) {
118 0         0 die "Got unexpected end of document";
119             }
120 2277         4610 my $docs = $self->docs;
121 2277         5030 push @$docs, $last->{ref}->[0];
122 2277         5879 $self->set_anchors({});
123 2277         5173 $self->set_stack([]);
124             }
125              
126             sub mapping_start_event {
127 1303     1303 1 2338 my ($self, $event) = @_;
128 1303         2754 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
129 1303         5461 my $ref = {
130             type => 'mapping',
131             ref => [],
132             data => \$data,
133             event => $event,
134             on_data => $on_data,
135             };
136 1303         2945 my $stack = $self->stack;
137              
138 1303         2670 my $preserve_order = $self->preserve_order;
139 1303         2430 my $preserve_style = $self->preserve_flow_style;
140 1303         2256 my $preserve_alias = $self->preserve_alias;
141 1303 100 100     6886 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
142 24         155 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
143             }
144 1303 100       2892 if ($preserve_style) {
145 15         30 my $t = tied %$data;
146 15         33 $t->{style} = $event->{style};
147             }
148              
149 1303         2237 push @$stack, $ref;
150 1303 100       4806 if (defined(my $anchor = $event->{anchor})) {
151 77 100       171 if ($preserve_alias) {
152 6         17 my $t = tied %$data;
153 6 100       11 unless (exists $self->anchors->{ $anchor }) {
154             # Repeated anchors cannot be preserved
155 5         12 $t->{alias} = $anchor;
156             }
157             }
158 77         240 $self->anchors->{ $anchor } = { data => $ref->{data} };
159             }
160             }
161              
162             sub mapping_end_event {
163 1291     1291 1 2383 my ($self, $event) = @_;
164 1291         2513 my $stack = $self->stack;
165              
166 1291         2245 my $last = pop @$stack;
167 1291         2046 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1291         2957  
168 1291 50       3108 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
169              
170 1291         2209 my @merge_keys;
171             my @ref;
172 1291         3123 for (my $i = 0; $i < @$ref; $i += 2) {
173 2513         4160 my $key = $ref->[ $i ];
174 2513 100       4410 if (ref $key eq 'YAML::PP::Type::MergeKey') {
175 6         14 my $merge = $ref->[ $i + 1 ];
176 6 100 100     47 if ((reftype($merge) || '') eq 'HASH') {
    100 100        
177 1         5 push @merge_keys, $merge;
178             }
179             elsif ((reftype($merge) || '') eq 'ARRAY') {
180 4         12 for my $item (@$merge) {
181 7 100 100     22 if ((reftype($item) || '') eq 'HASH') {
182 5         9 push @merge_keys, $item;
183             }
184             else {
185 2         26 die "Expected hash for merge key";
186             }
187             }
188             }
189             else {
190 1         12 die "Expected hash or array for merge key";
191             }
192             }
193             else {
194 2507         7118 push @ref, $key, $ref->[ $i + 1 ];
195             }
196             }
197 1288         2738 for my $merge (@merge_keys) {
198 6         17 for my $key (keys %$merge) {
199 9 100       22 unless (exists $$data->{ $key }) {
200 8         17 $$data->{ $key } = $merge->{ $key };
201             }
202             }
203             }
204             my $on_data = $last->{on_data} || sub {
205 1248     1248   2329 my ($self, $hash, $items) = @_;
206 1248         1737 my %seen;
207 1248         3940 for (my $i = 0; $i < @$items; $i += 2) {
208 2443         5278 my ($key, $value) = @$items[ $i, $i + 1 ];
209 2443         5104 $key = '' unless defined $key;
210 2443         4532 if (ref $key) {
211 71         189 $key = $self->stringify_complex($key);
212             }
213 2443         8282 if ($seen{ $key }++ and not $self->duplicate_keys) {
214 2         283 croak "Duplicate key '$key'";
215             }
216 2441         8209 $$hash->{ $key } = $value;
217             }
218 1288   100     9045 };
219 1288         4294 $on_data->($self, $data, \@ref);
220 1276         1968 push @{ $stack->[-1]->{ref} }, $$data;
  1276         3103  
221 1276 100       3219 if (defined(my $anchor = $last->{event}->{anchor})) {
222 73         185 $self->anchors->{ $anchor }->{finished} = 1;
223             }
224 1276         11533 return;
225             }
226              
227             sub sequence_start_event {
228 1213     1213 1 2193 my ($self, $event) = @_;
229 1213         2475 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
230 1213         5483 my $ref = {
231             type => 'sequence',
232             ref => [],
233             data => \$data,
234             event => $event,
235             on_data => $on_data,
236             };
237 1213         2781 my $stack = $self->stack;
238              
239 1213         2349 my $preserve_style = $self->preserve_flow_style;
240 1213         2099 my $preserve_alias = $self->preserve_alias;
241 1213 100 66     4130 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
242 9         51 tie @$data, 'YAML::PP::Preserve::Array', @$data;
243 9         20 my $t = tied @$data;
244 9         39 $t->{style} = $event->{style};
245             }
246              
247 1213         2109 push @$stack, $ref;
248 1213 100       4550 if (defined(my $anchor = $event->{anchor})) {
249 30 100       90 if ($preserve_alias) {
250 6         10 my $t = tied @$data;
251 6 100       16 unless (exists $self->anchors->{ $anchor }) {
252             # Repeated anchors cannot be preserved
253 5         25 $t->{alias} = $anchor;
254             }
255             }
256 30         97 $self->anchors->{ $anchor } = { data => $ref->{data} };
257             }
258             }
259              
260             sub sequence_end_event {
261 1205     1205 1 2268 my ($self, $event) = @_;
262 1205         2381 my $stack = $self->stack;
263 1205         2134 my $last = pop @$stack;
264 1205 50       3126 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
265 1205         1940 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1205         2562  
266              
267             my $on_data = $last->{on_data} || sub {
268 1204     1204   2071 my ($self, $array, $items) = @_;
269 1204         3289 push @$$array, @$items;
270 1205   100     8043 };
271 1205         3472 $on_data->($self, $data, $ref);
272 1205         1720 push @{ $stack->[-1]->{ref} }, $$data;
  1205         2626  
273 1205 100       3235 if (defined(my $anchor = $last->{event}->{anchor})) {
274 30         102 my $test = $self->anchors->{ $anchor };
275 30         80 $self->anchors->{ $anchor }->{finished} = 1;
276             }
277 1205         6686 return;
278             }
279              
280       2188 1   sub stream_start_event {}
281              
282       2160 1   sub stream_end_event {}
283              
284             sub scalar_event {
285 7639     7639 1 13037 my ($self, $event) = @_;
286 7639         10183 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
287 7639         15488 my $value = $self->schema->load_scalar($self, $event);
288 7630         17822 my $last = $self->stack->[-1];
289 7630         15148 my $preserve_alias = $self->preserve_alias;
290 7630         13810 my $preserve_style = $self->preserve_scalar_style;
291 7630 100 100     29281 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
292             my %args = (
293             value => $value,
294             tag => $event->{tag},
295 83         270 );
296 83 100       163 if ($preserve_style) {
297 17         36 $args{style} = $event->{style};
298             }
299 83 100 100     263 if ($preserve_alias and defined $event->{anchor}) {
300 6         11 my $anchor = $event->{anchor};
301 6 100       14 unless (exists $self->anchors->{ $anchor }) {
302             # Repeated anchors cannot be preserved
303 5         20 $args{alias} = $event->{anchor};
304             }
305             }
306 83         326 $value = YAML::PP::Preserve::Scalar->new( %args );
307             }
308 7630 100       17657 if (defined (my $name = $event->{anchor})) {
309 185         752 $self->anchors->{ $name } = { data => \$value, finished => 1 };
310             }
311 7630         10660 push @{ $last->{ref} }, $value;
  7630         25123  
312             }
313              
314             sub alias_event {
315 145     145 1 323 my ($self, $event) = @_;
316 145         226 my $value;
317 145         308 my $name = $event->{value};
318 145 100       338 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 144 100       360 unless ($anchor->{finished} ) {
322 9         33 my $cyclic_refs = $self->cyclic_refs;
323 9 100       33 if ($cyclic_refs ne 'allow') {
324 4 100       10 if ($cyclic_refs eq 'fatal') {
325 2         41 croak "Found cyclic ref for alias '$name'";
326             }
327 2 100       13 if ($cyclic_refs eq 'warn') {
    50          
328 1         5 $anchor = { data => \undef };
329 1         17 warn "Found cyclic ref for alias '$name'";
330             }
331             elsif ($cyclic_refs eq 'ignore') {
332 1         5 $anchor = { data => \undef };
333             }
334             }
335             }
336 142         372 $value = $anchor->{data};
337             }
338             else {
339 1         176 croak "No anchor defined for alias '$name'";
340             }
341 142         289 my $last = $self->stack->[-1];
342 142         219 push @{ $last->{ref} }, $$value;
  142         460  
343             }
344              
345             sub stringify_complex {
346 75     75 1 146 my ($self, $data) = @_;
347 75 50 66     210 return $data if (
      66        
348             ref $data eq 'YAML::PP::Preserve::Scalar'
349             and ($self->preserve_scalar_style or $self->preserve_alias)
350             );
351 25         155 require Data::Dumper;
352 25         86 local $Data::Dumper::Quotekeys = 0;
353 25         58 local $Data::Dumper::Terse = 1;
354 25         82 local $Data::Dumper::Indent = 0;
355 25         47 local $Data::Dumper::Useqq = 0;
356 25         52 local $Data::Dumper::Sortkeys = 1;
357 25         178 my $string = Data::Dumper->Dump([$data], ['data']);
358 25         1630 $string =~ s/^\$data = //;
359 25         84 return $string;
360             }
361              
362             1;
363              
364             __END__