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   272 use strict;
  35         77  
  35         1061  
3 35     35   199 use warnings;
  35         73  
  35         1762  
4             package YAML::PP::Constructor;
5              
6             our $VERSION = '0.036_001'; # TRIAL VERSION
7              
8 35     35   1181 use YAML::PP;
  35         75  
  35         1045  
9 35         2250 use YAML::PP::Common qw/
10             PRESERVE_ORDER PRESERVE_SCALAR_STYLE PRESERVE_FLOW_STYLE PRESERVE_ALIAS
11 35     35   202 /;
  35         77  
12 35     35   256 use Scalar::Util qw/ reftype /;
  35         75  
  35         1759  
13 35     35   243 use Carp qw/ croak /;
  35         78  
  35         2686  
14              
15 35 50 33 35   263 use constant DEBUG => ($ENV{YAML_PP_LOAD_DEBUG} or $ENV{YAML_PP_LOAD_TRACE}) ? 1 : 0;
  35         76  
  35         3367  
16 35 50   35   266 use constant TRACE => $ENV{YAML_PP_LOAD_TRACE} ? 1 : 0;
  35         96  
  35         98257  
17              
18             my %cyclic_refs = qw/ allow 1 ignore 1 warn 1 fatal 1 /;
19              
20             sub new {
21 748     748 1 2637 my ($class, %args) = @_;
22              
23 748         1398 my $default_yaml_version = delete $args{default_yaml_version};
24 748         1262 my $duplicate_keys = delete $args{duplicate_keys};
25 748 100       2023 unless (defined $duplicate_keys) {
26 436         663 $duplicate_keys = 0;
27             }
28 748   100     2410 my $preserve = delete $args{preserve} || 0;
29 748 100       1666 if ($preserve == 1) {
30 1         2 $preserve = PRESERVE_ORDER | PRESERVE_SCALAR_STYLE | PRESERVE_FLOW_STYLE | PRESERVE_ALIAS;
31             }
32 748   50     1634 my $cyclic_refs = delete $args{cyclic_refs} || 'fatal';
33             die "Invalid value for cyclic_refs: $cyclic_refs"
34 748 100       1917 unless $cyclic_refs{ $cyclic_refs };
35 747         1171 my $schemas = delete $args{schemas};
36              
37 747 50       1895 if (keys %args) {
38 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
39             }
40              
41 747         3040 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         2157 $self->init;
49 747         2743 return $self;
50             }
51              
52             sub clone {
53 9     9 0 21 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         29 };
61 9         41 return bless $clone, ref $self;
62             }
63              
64             sub init {
65 2935     2935 1 4988 my ($self) = @_;
66 2935         7940 $self->set_docs([]);
67 2935         7845 $self->set_stack([]);
68 2935         7202 $self->set_anchors({});
69 2935         5854 $self->set_yaml_version($self->default_yaml_version);
70 2935         5504 $self->set_schema($self->schemas->{ $self->yaml_version } );
71             }
72              
73 4437     4437 1 7941 sub docs { return $_[0]->{docs} }
74 17366     17366 1 31490 sub stack { return $_[0]->{stack} }
75 588     588 1 1692 sub anchors { return $_[0]->{anchors} }
76 2935     2935 1 6823 sub set_docs { $_[0]->{docs} = $_[1] }
77 5212     5212 1 13257 sub set_stack { $_[0]->{stack} = $_[1] }
78 5212     5212 1 10047 sub set_anchors { $_[0]->{anchors} = $_[1] }
79 2969     2969 0 6094 sub schemas { return $_[0]->{schemas} }
80 10155     10155 1 31273 sub schema { return $_[0]->{schema} }
81 2969     2969 1 6508 sub set_schema { $_[0]->{schema} = $_[1] }
82 18     18 1 98 sub cyclic_refs { return $_[0]->{cyclic_refs} }
83 0     0 1 0 sub set_cyclic_refs { $_[0]->{cyclic_refs} = $_[1] }
84 2935     2935 0 7229 sub yaml_version { return $_[0]->{yaml_version} }
85 2969     2969 0 5039 sub set_yaml_version { $_[0]->{yaml_version} = $_[1] }
86 2959     2959 0 7811 sub default_yaml_version { return $_[0]->{default_yaml_version} }
87 1303     1303 0 2548 sub preserve_order { return $_[0]->{preserve} & PRESERVE_ORDER }
88 7680     7680 0 11975 sub preserve_scalar_style { return $_[0]->{preserve} & PRESERVE_SCALAR_STYLE }
89 2516     2516 0 4325 sub preserve_flow_style { return $_[0]->{preserve} & PRESERVE_FLOW_STYLE }
90 10190     10190 0 17710 sub preserve_alias { return $_[0]->{preserve} & PRESERVE_ALIAS }
91 5     5 0 18 sub duplicate_keys { return $_[0]->{duplicate_keys} }
92              
93             sub document_start_event {
94 2305     2305 1 4142 my ($self, $event) = @_;
95 2305         4813 my $stack = $self->stack;
96 2305 100       5299 if ($event->{version_directive}) {
97 34         69 my $version = $event->{version_directive};
98 34         116 $version = "$version->{major}.$version->{minor}";
99 34 100       109 if ($self->{schemas}->{ $version }) {
100 22         64 $self->set_yaml_version($version);
101 22         60 $self->set_schema($self->schemas->{ $version });
102             }
103             else {
104 12         32 $self->set_yaml_version($self->default_yaml_version);
105 12         31 $self->set_schema($self->schemas->{ $self->default_yaml_version });
106             }
107             }
108 2305         4316 my $ref = [];
109 2305         10228 push @$stack, { type => 'document', ref => $ref, data => $ref, event => $event };
110             }
111              
112             sub document_end_event {
113 2277     2277 1 4072 my ($self, $event) = @_;
114 2277         5982 my $stack = $self->stack;
115 2277         4064 my $last = pop @$stack;
116 2277 50       5692 $last->{type} eq 'document' or die "Expected mapping, but got $last->{type}";
117 2277 50       5221 if (@$stack) {
118 0         0 die "Got unexpected end of document";
119             }
120 2277         4319 my $docs = $self->docs;
121 2277         5202 push @$docs, $last->{ref}->[0];
122 2277         5813 $self->set_anchors({});
123 2277         4836 $self->set_stack([]);
124             }
125              
126             sub mapping_start_event {
127 1303     1303 1 2349 my ($self, $event) = @_;
128 1303         2737 my ($data, $on_data) = $self->schema->create_mapping($self, $event);
129 1303         5654 my $ref = {
130             type => 'mapping',
131             ref => [],
132             data => \$data,
133             event => $event,
134             on_data => $on_data,
135             };
136 1303         2786 my $stack = $self->stack;
137              
138 1303         2557 my $preserve_order = $self->preserve_order;
139 1303         2562 my $preserve_style = $self->preserve_flow_style;
140 1303         2362 my $preserve_alias = $self->preserve_alias;
141 1303 100 100     7008 if (($preserve_order or $preserve_style or $preserve_alias) and not tied(%$data)) {
      66        
142 24         137 tie %$data, 'YAML::PP::Preserve::Hash', %$data;
143             }
144 1303 100       2778 if ($preserve_style) {
145 15         32 my $t = tied %$data;
146 15         33 $t->{style} = $event->{style};
147             }
148              
149 1303         2526 push @$stack, $ref;
150 1303 100       4882 if (defined(my $anchor = $event->{anchor})) {
151 77 100       189 if ($preserve_alias) {
152 6         10 my $t = tied %$data;
153 6 100       14 unless (exists $self->anchors->{ $anchor }) {
154             # Repeated anchors cannot be preserved
155 5         11 $t->{alias} = $anchor;
156             }
157             }
158 77         283 $self->anchors->{ $anchor } = { data => $ref->{data} };
159             }
160             }
161              
162             sub mapping_end_event {
163 1291     1291 1 2285 my ($self, $event) = @_;
164 1291         2460 my $stack = $self->stack;
165              
166 1291         2112 my $last = pop @$stack;
167 1291         2090 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1291         2741  
168 1291 50       3509 $last->{type} eq 'mapping' or die "Expected mapping, but got $last->{type}";
169              
170 1291         2097 my @merge_keys;
171             my @ref;
172 1291         3180 for (my $i = 0; $i < @$ref; $i += 2) {
173 2513         4116 my $key = $ref->[ $i ];
174 2513 100       4629 if (ref $key eq 'YAML::PP::Type::MergeKey') {
175 6         13 my $merge = $ref->[ $i + 1 ];
176 6 100 100     50 if ((reftype($merge) || '') eq 'HASH') {
    100 100        
177 1         4 push @merge_keys, $merge;
178             }
179             elsif ((reftype($merge) || '') eq 'ARRAY') {
180 4         11 for my $item (@$merge) {
181 7 100 100     27 if ((reftype($item) || '') eq 'HASH') {
182 5         11 push @merge_keys, $item;
183             }
184             else {
185 2         25 die "Expected hash for merge key";
186             }
187             }
188             }
189             else {
190 1         13 die "Expected hash or array for merge key";
191             }
192             }
193             else {
194 2507         7309 push @ref, $key, $ref->[ $i + 1 ];
195             }
196             }
197 1288         2645 for my $merge (@merge_keys) {
198 6         19 for my $key (keys %$merge) {
199 9 100       22 unless (exists $$data->{ $key }) {
200 8         24 $$data->{ $key } = $merge->{ $key };
201             }
202             }
203             }
204             my $on_data = $last->{on_data} || sub {
205 1248     1248   2373 my ($self, $hash, $items) = @_;
206 1248         1810 my %seen;
207 1248         3830 for (my $i = 0; $i < @$items; $i += 2) {
208 2443         5773 my ($key, $value) = @$items[ $i, $i + 1 ];
209 2443         5310 $key = '' unless defined $key;
210 2443         4507 if (ref $key) {
211 71         170 $key = $self->stringify_complex($key);
212             }
213 2443         8394 if ($seen{ $key }++ and not $self->duplicate_keys) {
214 2         276 croak "Duplicate key '$key'";
215             }
216 2441         8191 $$hash->{ $key } = $value;
217             }
218 1288   100     9199 };
219 1288         4014 $on_data->($self, $data, \@ref);
220 1276         1986 push @{ $stack->[-1]->{ref} }, $$data;
  1276         3077  
221 1276 100       3216 if (defined(my $anchor = $last->{event}->{anchor})) {
222 73         189 $self->anchors->{ $anchor }->{finished} = 1;
223             }
224 1276         11616 return;
225             }
226              
227             sub sequence_start_event {
228 1213     1213 1 2206 my ($self, $event) = @_;
229 1213         2519 my ($data, $on_data) = $self->schema->create_sequence($self, $event);
230 1213         5347 my $ref = {
231             type => 'sequence',
232             ref => [],
233             data => \$data,
234             event => $event,
235             on_data => $on_data,
236             };
237 1213         2664 my $stack = $self->stack;
238              
239 1213         2333 my $preserve_style = $self->preserve_flow_style;
240 1213         2164 my $preserve_alias = $self->preserve_alias;
241 1213 100 66     4131 if ($preserve_style or $preserve_alias and not tied(@$data)) {
      100        
242 9         58 tie @$data, 'YAML::PP::Preserve::Array', @$data;
243 9         16 my $t = tied @$data;
244 9         28 $t->{style} = $event->{style};
245             }
246              
247 1213         2018 push @$stack, $ref;
248 1213 100       4638 if (defined(my $anchor = $event->{anchor})) {
249 30 100       120 if ($preserve_alias) {
250 6         15 my $t = tied @$data;
251 6 100       11 unless (exists $self->anchors->{ $anchor }) {
252             # Repeated anchors cannot be preserved
253 5         24 $t->{alias} = $anchor;
254             }
255             }
256 30         111 $self->anchors->{ $anchor } = { data => $ref->{data} };
257             }
258             }
259              
260             sub sequence_end_event {
261 1205     1205 1 2230 my ($self, $event) = @_;
262 1205         2337 my $stack = $self->stack;
263 1205         2121 my $last = pop @$stack;
264 1205 50       3122 $last->{type} eq 'sequence' or die "Expected mapping, but got $last->{type}";
265 1205         1912 my ($ref, $data) = @{ $last }{qw/ ref data /};
  1205         2798  
266              
267             my $on_data = $last->{on_data} || sub {
268 1204     1204   2256 my ($self, $array, $items) = @_;
269 1204         3369 push @$$array, @$items;
270 1205   100     8112 };
271 1205         3546 $on_data->($self, $data, $ref);
272 1205         1694 push @{ $stack->[-1]->{ref} }, $$data;
  1205         2827  
273 1205 100       3174 if (defined(my $anchor = $last->{event}->{anchor})) {
274 30         85 my $test = $self->anchors->{ $anchor };
275 30         78 $self->anchors->{ $anchor }->{finished} = 1;
276             }
277 1205         6712 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 12970 my ($self, $event) = @_;
286 7639         11285 DEBUG and warn "CONTENT $event->{value} ($event->{style})\n";
287 7639         15659 my $value = $self->schema->load_scalar($self, $event);
288 7630         17442 my $last = $self->stack->[-1];
289 7630         13890 my $preserve_alias = $self->preserve_alias;
290 7630         13426 my $preserve_style = $self->preserve_scalar_style;
291 7630 100 100     29745 if (($preserve_style or $preserve_alias) and not ref $value) {
      66        
292             my %args = (
293             value => $value,
294             tag => $event->{tag},
295 83         266 );
296 83 100       172 if ($preserve_style) {
297 17         38 $args{style} = $event->{style};
298             }
299 83 100 100     237 if ($preserve_alias and defined $event->{anchor}) {
300 6         12 my $anchor = $event->{anchor};
301 6 100       13 unless (exists $self->anchors->{ $anchor }) {
302             # Repeated anchors cannot be preserved
303 5         11 $args{alias} = $event->{anchor};
304             }
305             }
306 83         325 $value = YAML::PP::Preserve::Scalar->new( %args );
307             }
308 7630 100       17661 if (defined (my $name = $event->{anchor})) {
309 185         712 $self->anchors->{ $name } = { data => \$value, finished => 1 };
310             }
311 7630         11313 push @{ $last->{ref} }, $value;
  7630         24907  
312             }
313              
314             sub alias_event {
315 145     145 1 287 my ($self, $event) = @_;
316 145         223 my $value;
317 145         262 my $name = $event->{value};
318 145 100       342 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       380 unless ($anchor->{finished} ) {
322 9         40 my $cyclic_refs = $self->cyclic_refs;
323 9 100       34 if ($cyclic_refs ne 'allow') {
324 4 100       15 if ($cyclic_refs eq 'fatal') {
325 2         52 croak "Found cyclic ref for alias '$name'";
326             }
327 2 100       10 if ($cyclic_refs eq 'warn') {
    50          
328 1         4 $anchor = { data => \undef };
329 1         20 warn "Found cyclic ref for alias '$name'";
330             }
331             elsif ($cyclic_refs eq 'ignore') {
332 1         4 $anchor = { data => \undef };
333             }
334             }
335             }
336 142         337 $value = $anchor->{data};
337             }
338             else {
339 1         188 croak "No anchor defined for alias '$name'";
340             }
341 142         307 my $last = $self->stack->[-1];
342 142         235 push @{ $last->{ref} }, $$value;
  142         489  
343             }
344              
345             sub stringify_complex {
346 75     75 1 144 my ($self, $data) = @_;
347 75 50 66     216 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         160 require Data::Dumper;
352 25         68 local $Data::Dumper::Quotekeys = 0;
353 25         51 local $Data::Dumper::Terse = 1;
354 25         55 local $Data::Dumper::Indent = 0;
355 25         47 local $Data::Dumper::Useqq = 0;
356 25         45 local $Data::Dumper::Sortkeys = 1;
357 25         158 my $string = Data::Dumper->Dump([$data], ['data']);
358 25         1522 $string =~ s/^\$data = //;
359 25         73 return $string;
360             }
361              
362             1;
363              
364             __END__