File Coverage

blib/lib/JSONLD.pm
Criterion Covered Total %
statement 401 2753 14.5
branch 163 2796 5.8
condition 55 747 7.3
subroutine 42 99 42.4
pod 13 16 81.2
total 674 6411 10.5


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             JSONLD - A toolkit for transforming JSON-LD data.
4              
5             =head1 VERSION
6              
7             This document describes JSONLD version 0.005_01.
8              
9             =head1 SYNOPSIS
10              
11             use v5.14;
12             use JSON;
13             use JSONLD;
14            
15             my $infile = 'test.jsonld';
16             open(my $fh, '<', $infile) or die $!;
17             my $content = do { local($/); <$fh> };
18             my $data = JSON->new()->boolean_values(0, 1)->decode($content);
19            
20             my $jld = JSONLD->new();
21             my $expanded = $jld->expand($data);
22              
23             =head1 DESCRIPTION
24              
25             This module implements part of the JSON-LD 1.1 standard for manipulating JSON
26             data as linked data.
27              
28             This version provides full support for the JSON-LD 1.1 "Expansion" and
29             "toRdf" transformations (the latter primarily being useful through a subclass
30             of JSON-LD, such as that provided by L<AtteanX::Parser::JSONLD>).
31             Partial support for the "Compaction" transformation is provided, but it
32             contains many known deficiencies. Full support for "Compaction" may be
33             forthcoming in a future release.
34             No other JSON-LD transformation are supported at this time.
35              
36             =head1 METHODS
37              
38             =over 4
39              
40             =cut
41              
42             package JSONLD {
43 2     2   273505 use v5.14;
  2         14  
44 2     2   1220 use autodie;
  2         30281  
  2         10  
45             our $VERSION = '0.005_01';
46 2     2   15700 use utf8;
  2         37  
  2         10  
47 2     2   1344 use Moo;
  2         20968  
  2         10  
48 2     2   4449 use LWP;
  2         107372  
  2         91  
49 2     2   16 use List::Util qw(all any);
  2         5  
  2         171  
50 2     2   1634 use JSON;
  2         21736  
  2         14  
51 2     2   298 use JSON qw(decode_json);
  2         6  
  2         17  
52 2     2   1342 use IRI;
  2         346141  
  2         391  
53 2     2   1163 use FindBin qw($Bin);
  2         2233  
  2         286  
54 2     2   16 use File::Spec;
  2         6  
  2         45  
55 2     2   10 use File::Glob qw(bsd_glob);
  2         4  
  2         273  
56 2     2   1398 use Encode qw(encode decode_utf8);
  2         25188  
  2         173  
57 2     2   19 use Data::Dumper;
  2         8  
  2         109  
58 2     2   18 use Clone 'clone';
  2         4  
  2         112  
59 2     2   12 use Carp qw(confess);
  2         11  
  2         92  
60 2     2   17 use B qw(svref_2object SVf_IOK SVf_POK SVf_NOK SVf_IOK);
  2         4  
  2         163  
61 2     2   1078 use namespace::clean;
  2         20431  
  2         13  
62             # use Debug::ShowStuff qw(indent println);
63       0 0   sub println ($) {}
64       15 0   sub indent {}
65            
66             has 'base_iri' => (is => 'rw', required => 0, default => sub { IRI->new('http://example.org/') });
67             has 'processing_mode' => (is => 'ro', default => 'json-ld-1.1');
68             has 'max_remote_contexts' => (is => 'rw', default => 10);
69             has 'parsed_remote_contexts' => (is => 'rw', default => sub { +{} });
70             has 'rdf_direction' => (is => 'rw');
71             has 'identifier_map' => (is => 'rw', default => sub { +{} });
72             has 'next_identifier_id' => (is => 'rw', default => 0);
73             has 'default_language' => (is => 'rw');
74             has 'default_base_direction' => (is => 'rw');
75            
76             our $debug = 0;
77             my %keywords = map { $_ => 1 } qw(: @base @container @context @direction @graph @id @import @included @index @json @language @list @nest @none @prefix @propagate @protected @reverse @set @type @value @version @vocab);
78            
79             =item C<< compact( $data, [$context] ) >>
80              
81             Returns the JSON-LD compaction of C<< $data >>, using the optionally supplied
82             C<< $context >>.
83              
84             NOTE: Support for JSON-LD Compaction is not fully supported in this version.
85              
86             =cut
87              
88             sub compact {
89 0     0 1 0 my $self = shift;
90 0         0 my $d = shift;
91 0   0     0 my $context = shift // {};
92 0         0 my %args = $self->_default_options(@_);
93            
94            
95 0         0 my $expanded_input = do {
96 0         0 local($debug) = 0;
97 0         0 $self->expand($d, ordered => 0);
98             };
99 0 0       0 println(Data::Dumper->Dump([$context, $expanded_input], [qw(context expanded_input)])) if $debug;
100            
101 0         0 my $ctx = {
102             '@base' => $self->base_iri->abs, # TODO: not sure this follows the spec, but it's what makes test t0089 pass
103             };
104 0 0 0     0 if (ref($context) eq 'HASH' and exists $context->{'@context'}) {
105 0         0 local($debug) = 0;
106 0         0 $ctx = $self->_4_1_2_ctx_processing($ctx, $context->{'@context'});
107             }
108 0 0       0 println(Data::Dumper->Dump([$ctx], ['activeCtx'])) if $debug;
109 0         0 my $inverseCtx = $self->_4_3_inverse_context_creation($ctx);
110            
111             # warn "Compacting...";
112 0         0 my $c = $self->_compact($ctx, $inverseCtx, undef, $expanded_input, %args);
113              
114 0   0     0 my $out_ctx = $context->{'@context'} || {};
115 0 0 0     0 if ((ref($out_ctx) eq 'HASH' and scalar(@{[keys %$out_ctx]})) or (ref($out_ctx) eq 'ARRAY' and scalar(@$out_ctx))) {
  0   0     0  
      0        
116 0         0 $c->{'@context'} = $context->{'@context'};
117             }
118 0         0 return $c;
119             }
120            
121             =item C<< expand( $data, [expandContext => $ctx] ) >>
122              
123             Returns the JSON-LD expansion of C<< $data >>.
124              
125             If an C<< expandContext >> value is supplied, it is used to construct the
126             initial active context for the expansion process.
127              
128             =cut
129              
130             sub expand {
131 1     1 1 27 my $self = shift;
132 1         2 my $d = shift;
133 1         3 my %args = @_;
134            
135 1         26 my $ctx = {
136             '@base' => $self->base_iri->abs, # TODO: not sure this follows the spec, but it's what makes test t0089 pass
137             };
138 1 50       398 if (my $ec = $args{expandContext}) {
139 0 0 0     0 if (ref($ec) eq 'HASH' and exists $ec->{'@context'}) {
140 0         0 $ec = $ec->{'@context'};
141             }
142 0         0 $ctx = $self->_4_1_2_ctx_processing($ctx, $ec);
143             }
144             # warn "Expanding...";
145 1         9 return $self->_expand($ctx, undef, $d);
146             }
147            
148             =item C<< to_rdf( $data, [expandContext => $ctx] ) >>
149              
150             Returns the dataset generated by turning the JSON-LD expansion of C<< $data >>
151             into RDF.
152              
153             If an C<< expandContext >> value is supplied, it is passed to the C<< expand >>
154             function call that takes place internally prior to generating RDF.
155              
156             Note: this method must be called on a C<< JSONLD >> subclass which implements
157             the RDF-related methods:
158              
159             =over 4
160              
161             =item * C<< default_graph() >>
162            
163             =item * C<< new_dataset() >>
164            
165             =item * C<< new_triple($s, $p, $o) >>
166            
167             =item * C<< new_quad($s, $p, $o, $g) >>
168            
169             =item * C<< new_iri($value) >>
170              
171             =item * C<< new_graphname($value) >>
172            
173             =item * C<< new_blank( [$id] ) >>
174            
175             =item * C<< new_lang_literal($value, $lang) >>
176            
177             =item * C<< new_dt_literal($value, $datatype) >>
178              
179             =item * C<< add_quad($quad, $dataset) >>
180              
181             =back
182              
183             See L<AtteanX::Parser::JSONLD> for an API that provides this functionality.
184              
185             =cut
186              
187             sub to_rdf {
188 0     0 1 0 my $self = shift;
189 0         0 my $obj = shift;
190 0         0 my $expandedInput = do {
191 0         0 local($debug) = 0;
192 0         0 $self->expand($obj, @_);
193             };
194 0 0       0 println "to rdf " . Data::Dumper->Dump([$expandedInput], ['expandedInput']) if $debug;
195 0         0 my $dataset = $self->new_dataset;
196 0         0 my $map = {};
197 0         0 $self->identifier_map({});
198 0         0 $self->next_identifier_id(0);
199 0         0 $self->_7_2_2_nodemap_generation($expandedInput, $map);
200 0 0       0 println(Data::Dumper->Dump([$map], ['node_map'])) if $debug;
201 0         0 $self->_8_1_2_to_rdf($map, $dataset);
202 0         0 return $dataset;
203             }
204              
205             sub _compact {
206 0     0   0 my $self = shift;
207 0         0 my $activeCtx = shift;
208 0         0 my $inverseCtx = shift;
209 0         0 my $activeProp = shift;
210 0         0 my $element = shift;
211 0         0 my $result = $self->_6_1_2_compaction($activeCtx, $inverseCtx, $activeProp, $element, @_);
212              
213 0 0       0 if (ref($result) eq 'ARRAY') {
214             # https://github.com/w3c/json-ld-api/issues/318
215 0 0       0 if (scalar(@$result) == 0) {
216 0         0 $result = {};
217             } else {
218 0         0 my $key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@graph');
219 0         0 $result = { $key => $result };
220             }
221             }
222            
223 0         0 return $result;
224             }
225              
226             sub _expand {
227 1     1   4 my $self = shift;
228 1   50     5 my $ctx = shift // {};
229 1         3 my $prop = shift;
230 1         2 my $d = shift;
231 1         33 my $expanded_output = $self->_5_1_2_expansion($ctx, $prop, $d, @_);
232 1 50       5 if (ref($expanded_output) eq 'HASH') {
233 1         4 my @keys = keys %$expanded_output;
234 1 50 33     8 if (scalar(@keys) == 1 and $keys[0] eq '@graph') {
235 0 0       0 println "_expand 7.1" if $debug;
236 0         0 $expanded_output = $expanded_output->{'@graph'};
237             }
238             }
239            
240 1 50       3 unless (defined($expanded_output)) {
241 0 0       0 println "_expand 7.2" if $debug;
242 0         0 $expanded_output = [];
243             }
244            
245 1 50       4 if (ref($expanded_output) ne 'ARRAY') {
246 1 50       3 println "_expand 7.3" if $debug;
247 1         2 $expanded_output = [$expanded_output];
248             }
249            
250 1         4 return $expanded_output;
251             }
252            
253             sub _default_options {
254 0     0   0 my $self = shift;
255 0         0 my %args = @_;
256 0   0     0 $args{compactArrays} //= 1;
257 0   0     0 $args{compactToRelative} //= 1;
258 0   0     0 $args{ordered} //= 0;
259            
260 0         0 return %args;
261             }
262            
263             sub _values_from_scalar_or_array {
264 0     0   0 my $value = shift;
265 0 0       0 if (ref($value) eq 'ARRAY') {
266 0         0 return @$value;
267             } else {
268 0         0 return $value;
269             }
270             }
271            
272             sub _is_prefix_of {
273 0     0   0 my $v = shift;
274 0         0 my $big = shift;
275 0 0       0 return 0 unless defined($v);
276 0 0       0 return 0 unless defined($big);
277 0 0       0 return 0 if (length($v) > length($big));
278 0         0 return (substr($big, 0, length($v)) eq $v);
279             }
280              
281             sub _is_scalar {
282 2     2   5 my $v = shift;
283 2 100       9 return 1 unless (ref($v));
284 1 50       4 return 1 if JSON::is_bool($v);
285 1         10 return 0;
286             }
287            
288             sub _is_string {
289 5     5   9 my $v = shift;
290 5 50       7 return 0 unless defined($v);
291 5 100       16 return 0 if ref($v);
292 3         22 my $sv = svref_2object(\$v);
293 3         14 my $flags = $sv->FLAGS;
294 3         7 my $is_str = $flags & SVf_POK;
295 3   33     12 my $is_num = (($flags & SVf_NOK) or ($flags & SVf_IOK));
296 3   33     15 return ($is_str and not($is_num));
297             }
298            
299             sub _is_integer {
300 0     0   0 my $v = shift;
301 0 0       0 return 0 unless (_is_numeric($v));
302 0         0 my $i = int($v);
303 0         0 my $f = $v - $i;
304 0         0 return ($f == 0);
305             }
306              
307             sub _is_numeric {
308 0     0   0 my $v = shift;
309 0 0       0 return 0 unless defined($v);
310 0 0       0 return 0 if ref($v);
311 0         0 my $sv = svref_2object(\$v);
312 0         0 my $flags = $sv->FLAGS;
313 0   0     0 my $is_num = (($flags & SVf_NOK) or ($flags & SVf_IOK));
314 0         0 return $is_num;
315             }
316            
317             sub _is_abs_iri {
318 0     0   0 my $self = shift;
319 0         0 my $value = shift;
320 0 0       0 return 0 unless (length($value));
321 0         0 my $i = eval { IRI->new($value) };
  0         0  
322 0 0       0 unless ($i) {
323 0         0 return 0;
324             }
325 0   0     0 my $is_abs = (defined($i->scheme) and $value eq $i->abs);
326 0         0 return $is_abs;
327             }
328            
329             sub _is_iri {
330 1     1   2 my $self = shift;
331 1         1 my $value = shift;
332 1         2 my $i = eval { IRI->new(value => $value) };
  1         23  
333 1         331 my $err = $@;
334 1         3 my $is_iri = not($@);
335 1         12 return $is_iri;
336             }
337              
338             sub _make_relative_iri {
339 0     0   0 my $base = shift;
340 0         0 my $rel = shift;
341 0 0       0 println "Make relative IRI from: " . Data::Dumper->Dump([$base->abs, $rel->abs], [qw(base rel)]) if $debug;
342 0         0 my $r = $rel->rel($base)->abs;
343 0         0 return $r;
344             }
345            
346             sub _load_document {
347 0     0   0 my $self = shift;
348 0         0 my $url = shift;
349 0         0 my $profile = shift;
350 0         0 my $req_profile = shift;
351 0         0 my $ua = LWP::UserAgent->new();
352 0         0 my $resp = $ua->get($url);
353 0         0 return $resp;
354             }
355            
356             sub _cm_contains {
357 6     6   17 my $self = shift;
358 6         9 my $container_mapping = shift;
359 6         8 my $value = shift;
360 6 50       11 if (ref($container_mapping)) {
361 0 0       0 Carp::cluck unless (ref($container_mapping) eq 'ARRAY');
362 0         0 foreach my $m (@$container_mapping) {
363 0 0       0 return 1 if ($m eq $value);
364             }
365             } else {
366 6   33     48 return (defined($container_mapping) and $container_mapping eq $value);
367             }
368             }
369              
370             sub _cm_contains_any {
371 1     1   2 my $self = shift;
372 1         2 my $container_mapping = shift;
373 1         10 my @values = @_;
374 1         4 foreach my $value (@values) {
375 3 50       5 return 1 if ($self->_cm_contains($container_mapping, $value));
376             }
377 1         13 return 0;
378             }
379            
380             sub _ctx_term_defn {
381 12     12   23 my $self = shift;
382 12         13 my $ctx = shift;
383 12         19 my $term = shift;
384 12 50       27 confess "Bad context type in _ctx_term_defn: " . ref($ctx) unless (ref($ctx) eq 'HASH');
385 2     2   6669 no warnings 'uninitialized';
  2         6  
  2         18864  
386 12         40 return $ctx->{'terms'}{$term};
387             }
388              
389             sub _ctx_contains_protected_terms {
390 0     0   0 my $self = shift;
391 0         0 my $ctx = shift;
392 0         0 my @prot = $self->_ctx_protected_terms($ctx);
393 0         0 return scalar(@prot);
394             }
395            
396             sub _ctx_protected_terms {
397 0     0   0 my $self = shift;
398 0         0 my $ctx = shift;
399 0         0 my @protected;
400 0         0 foreach my $term (keys %{ $ctx->{'terms'} }) {
  0         0  
401 0 0       0 push(@protected, $term) if $ctx->{'terms'}{$term}{'protected'};
402             }
403 0         0 return @protected;
404             }
405            
406             sub _is_node_object {
407 0     0   0 my $self = shift;
408 0         0 my $value = shift;
409 0 0       0 return 0 unless (ref($value) eq 'HASH');
410 0         0 foreach my $p (qw(@value @list @set)) {
411 0 0       0 return 0 if (exists $value->{$p});
412             }
413             # TODO: check that value "is not the top-most map in the JSON-LD document consisting of no other entries than @graph and @context."
414 0         0 return 1;
415             }
416            
417             sub _is_value_object {
418 0     0   0 my $self = shift;
419 0         0 my $value = shift;
420 0 0       0 return 0 unless (ref($value) eq 'HASH');
421 0         0 return (exists $value->{'@value'});
422             }
423              
424             sub _is_default_object {
425 0     0   0 my $self = shift;
426 0         0 my $value = shift;
427 0 0       0 return 0 unless (ref($value) eq 'HASH');
428 0         0 return (exists $value->{'@default'});
429             }
430              
431             sub _is_list_object {
432 0     0   0 my $self = shift;
433 0         0 my $value = shift;
434 0 0       0 return 0 unless (ref($value) eq 'HASH');
435 0         0 return (exists $value->{'@list'});
436             }
437            
438             sub _is_graph_object {
439 0     0   0 my $self = shift;
440 0         0 my $value = shift;
441 0 0       0 return 0 unless (ref($value) eq 'HASH');
442 0         0 return (exists $value->{'@graph'});
443             }
444            
445             sub _is_simple_graph_object {
446 0     0   0 my $self = shift;
447 0         0 my $value = shift;
448 0 0       0 return 0 unless ($self->_is_graph_object($value));
449 0         0 return (not exists $value->{'@id'});
450             }
451            
452             sub _add_value {
453 0     0   0 my $self = shift;
454 0         0 my $object = shift;
455 0         0 my $key = shift;
456 0         0 my $value = shift;
457 0 0       0 unless (defined($value)) {
458 0         0 Carp::cluck "undefined value in add_value";
459             }
460 0         0 my %args = @_;
461 0   0     0 my $as_array = $args{'as_array'} // 0;
462 0 0 0     0 if ($as_array and ref($value) ne 'ARRAY') {
463 0         0 $value = [$value];
464             }
465            
466 0 0       0 if (not exists $object->{$key}) {
467 0         0 $object->{$key} = $value;
468             } else {
469 0 0       0 if (ref($object->{$key}) ne 'ARRAY') {
470 0         0 $object->{$key} = [$object->{$key}];
471             }
472            
473 0 0       0 if (ref($value) eq 'ARRAY') {
474 0         0 push(@{ $object->{$key} }, @$value);
  0         0  
475             } else {
476 0         0 push(@{ $object->{$key} }, $value);
  0         0  
477             }
478             }
479             }
480            
481             sub _is_well_formed_graph_node {
482 0     0   0 my $self = shift;
483 0         0 my $value = shift;
484 0 0       0 return 1 if $self->_is_well_formed_iri($value);
485 0 0       0 return 1 if ($value =~ /^_:/);
486 0         0 return 0;
487             }
488            
489             sub _is_well_formed_language {
490 0     0   0 my $self = shift;
491 0         0 my $value = shift;
492 0         0 my $ok = ($value =~ m/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/);
493 0 0       0 if (not $ok) {
494 0 0       0 println "not a well-formed language: $value\n" if $debug;
495             }
496 0         0 return $ok;
497             }
498            
499             sub _is_well_formed_datatype {
500 0     0   0 my $self = shift;
501 0         0 my $value = shift;
502 0 0       0 return 1 if ($value eq '@json');
503 0 0       0 return 1 if $self->_is_well_formed_iri($value);
504 0         0 return 0;
505             }
506            
507             sub _is_well_formed_iri {
508 0     0   0 my $self = shift;
509 0         0 my $value = shift;
510 0         0 my $ok = ($self->_is_abs_iri($value));
511 0 0       0 if (not $ok) {
512 0 0       0 println "not a well-formed IRI: $value\n" if $debug;
513             }
514 0         0 return $ok;
515             }
516            
517             sub _is_well_formed_graphname {
518 0     0   0 my $self = shift;
519 0         0 my $value = shift;
520 0   0     0 my $ok = ($self->_is_abs_iri($value) or ($value eq '@default') or ($value =~ /^_:(\w+)$/));
521 0 0       0 if (not $ok) {
522 0 0       0 println "not a well-formed graph name: $value\n" if $debug;
523             }
524 0         0 return $ok;
525             }
526            
527             sub _is_well_formed {
528 0     0   0 my $self = shift;
529 0         0 my $value = shift;
530 0 0       0 println "TODO: _is_well_formed: $value" if $debug;
531 0         0 return 1;
532             }
533              
534             sub _4_1_2_ctx_processing {
535 1 50   1   3 println "ENTER =================> _4_1_2_ctx_processing" if $debug;
536 1         3 my $__indent = indent();
537 1         4 my $self = shift;
538 1         2 my $activeCtx = shift;
539 1         19 my $localCtx = shift;
540 1         2 local($Data::Dumper::Indent) = 0;
541 1 50       3 println(Data::Dumper->Dump([$activeCtx], ['*activeCtx'])) if $debug;
542 1 50       3 println(Data::Dumper->Dump([$localCtx], ['*localCtx'])) if $debug;
543 1         3 my %args = @_;
544 1   50     5 my $propagate = $args{propagate} // 1;
545 1   50     5 my $remote_contexts = $args{remote_contexts} // [];
546 1   50     5 my $validate_scoped_context = $args{validate_scoped_context} // 1;
547 1   50     6 my $override_protected = $args{override_protected} // 0;
548 1   33     28 my $base_iri = $args{base_iri} // $self->base_iri->abs;
549              
550 1 50       70 println "1" if $debug;
551 1         33 my $result = clone($activeCtx); # 1
552 1 50       18 confess "Bad active context type in _4_1_2_ctx_processing: " . Dumper($activeCtx) unless (ref($activeCtx) eq 'HASH');
553 1 50 33     7 if (ref($localCtx) eq 'HASH' and exists $localCtx->{'@propagate'}) {
554 0 0       0 println "2" if $debug;
555 0         0 $propagate = (!!$localCtx->{'@propagate'}); # 2
556             }
557            
558 1 0 33     4 if (not($propagate) and not exists $result->{'previous_context'}) {
559 0 0       0 println "3" if $debug;
560 0         0 $result->{'previous_context'} = $activeCtx; # 3
561             }
562            
563 1 50       3 if (ref($localCtx) ne 'ARRAY') {
564 1 50       4 println "4" if $debug;
565 1         3 $localCtx = [$localCtx]; # 4
566             }
567            
568 1 50       3 println "5" if $debug;
569 1         5 foreach my $context (@$localCtx) {
570 1         2 my $__indent = indent();
571 1 50       3 println '-----------------------------------------------------------------' if $debug;
572 1 50       3 println "5 loop for each local context item" if $debug;
573 1 50       2 println(Data::Dumper->Dump([$context], ['*context'])) if $debug;
574 1 50       3 if (not(defined($context))) {
575             # 5.1
576 0 0       0 println "5.1" if $debug;
577 0 0 0     0 if (not($override_protected) and $self->_ctx_contains_protected_terms($activeCtx)) {
578 0         0 my @prot = $self->_ctx_protected_terms($activeCtx);
579 0 0       0 println "5.1.1 " . Data::Dumper->Dump([\@prot], ['protected_terms']) if $debug;
580 0         0 die 'invalid context nullification'; # 5.1.1
581             } else {
582 0 0       0 println "5.1.2 moving to next context" if $debug;
583 0         0 my $prev = $result;
584 0         0 $result = {
585             '@base' => $self->base_iri->abs, # TODO: not sure this follows the spec, but it's what makes test t0089 pass
586             };
587 0 0       0 if ($propagate) {
588 0         0 $result->{'previous_context'} = $prev;
589             }
590 0         0 next;
591             }
592             }
593              
594 1 50       5 if (not(ref($context))) {
595 0 0       0 println "5.2 $context" if $debug;
596            
597 0 0       0 println "5.2.1" if $debug;
598 0         0 $context = IRI->new(value => $context, base => $base_iri)->abs;
599            
600 0 0       0 if (scalar(@$remote_contexts) > $self->max_remote_contexts) {
601 0 0       0 println "5.2.2" if $debug;
602 0         0 die 'context overflow';
603             }
604            
605 0         0 my %already = map { $_ => 1 } @$remote_contexts;
  0         0  
606 0 0 0     0 if (not($validate_scoped_context) and $already{$context}) {
607 0         0 next;
608             }
609            
610 0 0       0 unless ($already{$context}) {
611 0         0 push(@$remote_contexts, $context);
612             }
613              
614 0         0 my $context_url = $context;
615 0 0       0 if (my $c = $self->parsed_remote_contexts->{$context}) {
616 0 0       0 println "5.2.3" if $debug;
617 0         0 $context = $c;
618             } else {
619 0 0       0 println "5.2.4 loading context from: $context_url" if $debug;
620 0         0 my $resp = $self->_load_document($context_url, 'http://www.w3.org/ns/json-ld#context', 'http://www.w3.org/ns/json-ld#context');
621 0 0       0 if (not $resp->is_success) {
622 0 0       0 println "5.2.5 " . $resp->status_line if $debug;
623 0         0 die 'loading remote context failed';
624             }
625 0 0       0 my $content = $resp->content_is_text ? $resp->decoded_content : decode_utf8($resp->decoded_content);
626 0         0 $context = eval { decode_json(encode('UTF-8', $content))->{'@context'} };
  0         0  
627 0 0       0 if ($@) {
628 0 0       0 println "5.2.5 $@" if $debug;
629 0         0 die 'loading remote context failed';
630             }
631 0         0 $self->parsed_remote_contexts->{$context_url} = $context;
632             }
633              
634 0 0       0 println "5.2.6" if $debug;
635 0         0 $result = $self->_4_1_2_ctx_processing($result, $context, remote_contexts => clone($remote_contexts), validate_scoped_context => $validate_scoped_context, base_iri => $context_url);
636              
637 0 0       0 println "5.2.7 moving to next context" if $debug;
638 0         0 next;
639             }
640              
641 1 50       5 if (ref($context) ne 'HASH') {
642 0 0       0 println "5.3" if $debug;
643 0         0 die "invalid_local_context"; # 5.3
644             }
645            
646 1 50       17 println "5.4" if $debug; # no-op
647              
648 1 50       5 if (exists $context->{'@version'}) {
649 0 0       0 println "5.5" if $debug;
650 0         0 my $v = $context->{'@version'};
651 0 0       0 if ($v ne '1.1') {
652 0 0       0 println "5.5.1" if $debug;
653 0         0 die 'invalid @version value'; # 5.5.1
654             }
655 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
656 0 0       0 println "5.5.2" if $debug;
657 0         0 die 'processing mode conflict';
658             }
659             }
660              
661 1 50       5 if (exists $context->{'@import'}) {
662 0 0       0 println "5.6" if $debug;
663 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
664 0 0       0 println "5.6.1" if $debug;
665 0         0 die 'invalid context entry';
666             }
667            
668 0         0 my $value = $context->{'@import'};
669 0 0       0 if (ref($value)) {
670 0 0       0 println "5.6.2" if $debug;
671 0         0 die 'invalid @import value';
672             }
673            
674 0 0       0 println "5.6.3 resolving \@import $value" if $debug;
675 0         0 my $import = IRI->new(value => $value, base => $self->base_iri)->abs;
676            
677 0 0       0 println "5.6.4 loading $import" if $debug;
678 0         0 my $resp = $self->_load_document($import, 'http://www.w3.org/ns/json-ld#context', 'http://www.w3.org/ns/json-ld#context');
679            
680 0 0       0 if (not $resp->is_success) {
681 0 0       0 println "5.6.5" if $debug;
682 0         0 die 'loading remote context failed';
683             }
684            
685 0 0       0 my $content = $resp->content_is_text ? $resp->decoded_content : decode_utf8($resp->decoded_content);
686 0         0 my $j = eval { decode_json(encode('UTF-8', $content))->{'@context'} };
  0         0  
687 0 0       0 if ($@) {
688 0 0       0 println "5.6.5 $@" if $debug;
689 0         0 die 'loading remote context failed';
690             }
691            
692 0 0       0 unless (ref($j) eq 'HASH') {
693 0 0       0 println "5.6.6" if $debug;
694 0         0 die 'invalid remote context';
695             }
696 0         0 my $import_context = $j;
697            
698 0 0       0 if (exists $import_context->{'@import'}) {
699 0 0       0 println "5.6.7" if $debug;
700 0         0 die 'invalid context entry';
701             }
702            
703 0 0       0 println "5.6.8" if $debug;
704 0         0 %$context = (%$import_context, %$context);
705 0 0       0 println(Data::Dumper->Dump([$context], ['context'])) if $debug;
706             }
707            
708 1 50 33     4 if (exists $context->{'@base'} and scalar(@$remote_contexts) == 0) {
709 0 0       0 println "5.7" if $debug;
710 0 0       0 println "5.7.1" if $debug;
711 0         0 my $value = $context->{'@base'};
712 0 0       0 println(Data::Dumper->Dump([$result], ['result'])) if $debug;
713            
714 0 0 0     0 if (not defined($value)) {
    0          
    0          
715 0 0       0 println "5.7.2" if $debug;
716 0         0 delete $result->{'@base'};
717             } elsif ($self->_is_abs_iri($value)) {
718 0 0       0 println "5.7.3 " . Data::Dumper->Dump([$value], ['base']) if $debug;
719 0         0 $result->{'@base'} = $value;
720             } elsif ($self->_is_iri($value) and defined($result->{'@base'})) {
721 0 0       0 println "5.7.4" if $debug;
722 0         0 my $base = IRI->new($result->{'@base'});
723 0         0 my $i = IRI->new(value => $value, base => $base);
724 0         0 $result->{'@base'} = $i->abs;
725             } else {
726 0 0       0 println "5.7.5" if $debug;
727 0         0 die 'invalid base IRI';
728             }
729             }
730            
731 1 50       2 if (exists $context->{'@vocab'}) {
732 1 50       3 println "5.8" if $debug;
733 1 50       3 println "5.8.1" if $debug;
734 1         12 my $value = $context->{'@vocab'}; # 5.8.1
735 1 50 33     12 if (not defined($value)) {
    50          
736 0 0       0 println "5.8.2" if $debug;
737 0         0 delete $result->{'@vocab'}; # 5.8.2
738             } elsif ($value =~ /^_/ or $self->_is_iri($value)) {
739 1 50       8 println "5.8.3" if $debug;
740 1         7 my $iri = $self->_5_2_2_iri_expansion($result, $value, vocab => 1, documentRelative => 1);
741 1         4 $result->{'@vocab'} = $iri;
742             } else {
743 0         0 die 'invalid vocab mapping';
744             }
745             }
746            
747 1 50       4 if (exists $context->{'@language'}) {
748 0 0       0 println "5.9" if $debug;
749 0 0       0 println "5.9.1 language = " . $context->{'@language'} if $debug;
750 0         0 my $value = $context->{'@language'};
751            
752 0 0       0 if (not defined($value)) {
    0          
753 0 0       0 println "5.9.2" if $debug;
754 0         0 delete $result->{'@language'};
755             } elsif (_is_string($value)) {
756 0 0       0 println "5.9.3 value is a string" if $debug;
757 0         0 $result->{'@language'} = $value;
758             # TODO: validate language tag against BCP47
759             } else {
760 0 0       0 println "5.9.3 value is NOT a string" if $debug;
761 0         0 die 'invalid default language';
762             }
763             }
764            
765 1 50       4 if (exists $context->{'@direction'}) {
766 0 0       0 println "5.10" if $debug;
767 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
768 0 0       0 println "5.10.1" if $debug;
769 0         0 die 'invalid context entry';
770             }
771            
772 0 0       0 println "5.10.2" if $debug;
773 0         0 my $value = $context->{'@direction'};
774            
775 0 0       0 if (not defined($value)) {
    0          
776 0 0       0 println "5.10.3" if $debug;
777 0         0 delete $result->{'@direction'};
778             } elsif (_is_string($value)) {
779 0 0       0 println "5.10.4 \@direction = $value" if $debug;
780 0 0 0     0 if ($value ne 'ltr' and $value ne 'rtl') {
781 0         0 die 'invalid base direction';
782             }
783 0         0 $result->{'@direction'} = $value;
784             }
785             }
786            
787 1 50       5 if (exists $context->{'@propagate'}) {
788 0 0       0 println "5.11" if $debug;
789 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
790 0 0       0 println "5.11.1" if $debug;
791 0         0 die 'invalid context entry';
792             }
793            
794 0         0 my $p = $context->{'@propagate'};
795 0 0 0     0 if ($p ne '1' and $p ne '0' and ref($p) ne 'JSON::PP::Boolean') { # boolean true or false
      0        
796 0 0       0 println "5.11.2" if $debug;
797 0         0 die 'invalid @propagate value';
798             }
799            
800 0 0       0 println "5.11.3" if $debug;
801             }
802            
803 1 50       6 println "5.12" if $debug;
804 1         3 my $defined = {}; # 5.12
805            
806 1         3 my @keys = reverse sort grep { $_ !~ /^[@](base|direction|import|language|propagate|protected|version|vocab)$/ } keys %$context;
  1         12  
807 1 50       3 println "5.13" if $debug;
808 1         6 foreach my $key (@keys) {
809 0         0 my $__indent = indent();
810 0 0       0 println "5.13 [$key]" if $debug;
811 0         0 my $value = $context->{$key};
812 0         0 $self->_4_2_2_create_term_definition($result, $context, $key, $defined, protected => $context->{'@protected'}, override_protected => $override_protected, remote_contexts => clone($remote_contexts), validate_scoped_context => $validate_scoped_context, base_iri => $base_iri); # 5.13
813             }
814             }
815              
816 1         3 local($Data::Dumper::Indent) = 1;
817 1 50       3 println "6 returning from _4_1_2_ctx_processing with " . Data::Dumper->Dump([$result], ['final_context']) if $debug;
818 1         4 return $result; # 6
819             }
820            
821             sub _4_2_2_create_term_definition {
822 0     0   0 my $self = shift;
823 0         0 my $activeCtx = shift;
824 0         0 my $localCtx = shift;
825 0         0 my $term = shift;
826 0   0     0 my $defined = shift // {};
827 0 0       0 println "ENTER =================> _4_2_2_create_term_definition('$term')" if $debug;
828 0         0 my $__indent = indent();
829 0         0 local($Data::Dumper::Indent) = 0;
830 0 0       0 println(Data::Dumper->Dump([$activeCtx], ['*activeCtx'])) if $debug;
831 0 0       0 println(Data::Dumper->Dump([$localCtx], ['*localCtx'])) if $debug;
832 0 0       0 println(Data::Dumper->Dump([$defined], ['*defined'])) if $debug;
833 0         0 my %args = @_;
834 0   0     0 my $protected = $args{protected} // 0;
835 0   0     0 my $override_protected = $args{override_protected} // 0;
836 0   0     0 my $remote_contexts = $args{remote_contexts} // [];
837 0   0     0 my $validate_scoped_context = $args{validate_scoped_context} // 1;
838 0   0     0 my $base_iri = $args{base_iri} // $self->base_iri->abs;
839            
840             # 4.2.2
841 0 0       0 if (exists ($defined->{$term})) {
842             # 1
843 0 0       0 println "1" if $debug;
844 0 0       0 if ($defined->{$term}) {
    0          
845 0 0       0 println "returning from _4_2_2_create_term_definition: term definition has already been created\n" if $debug;
846 0         0 return;
847             } elsif (exists $defined->{$term}) {
848 0         0 die "cyclic IRI mapping";
849             }
850             }
851            
852 0 0       0 println "2 [setting defined{$term} = 0]" if $debug;
853 0         0 my $term_copy = $term;
854 0 0       0 if ($term_copy eq '') {
855 0         0 die 'invalid term definition';
856             } else {
857 0         0 $defined->{$term} = 0; # 2
858             }
859            
860 0 0       0 println "3" if $debug;
861 0         0 my $value = clone($localCtx->{$term}); # 3
862 0 0       0 println "3 " . Data::Dumper->Dump([$value], ['value']) if $debug;
863            
864             # NOTE: the language interaction between 4 and 5 here is a mess. Unclear what "Otherwise" applies to. Similarly with the "Otherwise" that begins 7 below.
865 0 0 0     0 if ($self->processing_mode eq 'json-ld-1.1' and $term eq '@type') {
866             # 4
867 0 0       0 println "4" if $debug;
868 0 0       0 unless (ref($value) eq 'HASH') {
869 0         0 die 'keyword redefinition';
870             }
871 0         0 my @keys = grep { $_ ne '@protected' } keys %$value;
  0         0  
872 0 0 0     0 die 'keyword redefinition' unless (scalar(@keys) == 1 and $keys[0] eq '@container');
873 0 0       0 die 'keyword redefinition' unless ($value->{'@container'} eq '@set');
874             } else {
875             # 5
876 0 0       0 println "5" if $debug;
877 0 0       0 if (exists $keywords{$term}) {
878 0 0       0 println "5 keyword redefinition: $term" if $debug;
879 0         0 die 'keyword redefinition';
880             }
881 0 0       0 if ($term =~ /^@[A-Za-z]+$/) {
882             # https://www.w3.org/2018/json-ld-wg/Meetings/Minutes/2019/2019-09-20-json-ld#section5-2
883 0         0 warn "create term definition attempted on a term that looks like a keyword: $term\n";
884 0 0       0 println "5 returning so as to ignore a term that has the form of a keyword: $term" if $debug;
885 0         0 return;
886             }
887             }
888            
889 0 0       0 println "6" if $debug;
890 0         0 my $previous_defn = $self->_ctx_term_defn($activeCtx, $term); # 6
891 0         0 delete $activeCtx->{'terms'}{$term}; # https://github.com/w3c/json-ld-api/issues/176#issuecomment-545167708
892              
893 0         0 my $simple_term;
894 0 0       0 if (not(defined($value))) {
    0          
    0          
895 0 0       0 println "7" if $debug;
896 0         0 $value = {'@id' => undef}; # 7
897             } elsif (_is_string($value)) {
898             # 8
899 0 0       0 println "8 value = {'\@id' => $value}" if $debug;
900 0         0 $value = {'@id' => $value};
901 0         0 $simple_term = 1;
902             } elsif (ref($value) eq 'HASH') {
903 0 0       0 println "9" if $debug;
904 0         0 $simple_term = 0; # 9
905             } else {
906 0 0       0 println "9" if $debug;
907 0         0 die "invalid_term_definition"; # 9
908             }
909            
910 0 0       0 println "10" if $debug;
911 0         0 my $definition = {'__source_base_iri' => $base_iri}; # 10
912            
913 0 0 0     0 if ($value->{'@protected'}) {
    0          
914 0 0       0 println "11" if $debug;
915 0         0 $definition->{'protected'} = 1; # 11
916 0 0       0 println "11 TODO processing mode of json-ld-1.0" if $debug;
917             } elsif (not exists $value->{'@protected'} and $protected) {
918 0 0       0 println "12" if $debug;
919 0         0 $definition->{'protected'} = 1; # 12
920             }
921              
922 0 0       0 if (exists $value->{'@type'}) {
923             # 13
924 0 0       0 println "13" if $debug;
925 0         0 my $type = $value->{'@type'}; # 13.1
926 0 0       0 if (ref($type)) {
927 0 0       0 println "13.1" if $debug;
928 0         0 die "invalid_type_mapping"; # 13.1
929             }
930            
931 0 0       0 println "13.2" if $debug;
932 0         0 $type = $self->_5_2_2_iri_expansion($activeCtx, $type, vocab => 1, localCtx => $localCtx, 'defined' => $defined); # 13.2
933 0 0       0 println(Data::Dumper->Dump([$type], ['type'])) if $debug;
934              
935 0 0 0     0 if (($type eq '@json' or $type eq '@none') and $self->processing_mode eq 'json-ld-1.0') {
      0        
936             # https://github.com/w3c/json-ld-api/issues/259
937 0 0       0 println "13.3 " . Data::Dumper->Dump([$type], ['*type']) if $debug;
938 0         0 die 'invalid type mapping';
939             }
940            
941 0 0 0     0 if ($type ne '@id' and $type ne '@vocab' and $type ne '@none' and $type ne '@json' and not($self->_is_abs_iri($type))) {
      0        
      0        
      0        
942             # TODO: handle case "nor, if processing mode is json-ld-1.1, @json nor @none"
943 0 0       0 println "13.4 " . Data::Dumper->Dump([$type], ['*type']) if $debug;
944 0         0 die 'invalid type mapping'; # 13.4
945             }
946            
947 0 0       0 println "13.5" if $debug;
948 0         0 $definition->{'type_mapping'} = $type; # 13.5
949             }
950            
951 0 0       0 if (exists $value->{'@reverse'}) {
952             # 14
953 0 0       0 println "14" if $debug;
954 0 0 0     0 if (exists $value->{'@id'} or exists $value->{'@nest'}) {
955 0 0       0 println "14.1" if $debug;
956 0         0 die 'invalid reverse property'; # 14.1
957             }
958 0         0 my $reverse = $value->{'@reverse'};
959 0 0       0 if (ref($reverse)) {
960 0 0       0 println "14.2" if $debug;
961 0         0 die 'invalid IRI mapping'; # 14.2
962             }
963 0 0       0 if ($reverse =~ /^@[A-Za-z]+$/) {
964 0 0       0 println "14.3" if $debug;
965 0         0 warn '@reverse value looks like a keyword: ' . $reverse; # 14.3
966 0         0 return;
967             } else {
968             # 14.4
969 0 0       0 println "14.4" if $debug;
970 0         0 my $m = $self->_5_2_2_iri_expansion($activeCtx, $reverse, vocab => 1, localCtx => $localCtx, 'defined' => $defined);
971 0 0 0     0 if (not($self->_is_abs_iri($m)) and $m !~ /^:/) {
972 0         0 die 'invalid IRI mapping';
973             }
974 0         0 $definition->{'iri_mapping'} = $m;
975             }
976            
977 0 0       0 if (exists $value->{'@container'}) {
978             # 14.5
979 0 0       0 println "14.5" if $debug;
980 0         0 my $c = $value->{'@container'};
981 0 0 0     0 if ($c ne '@set' and $c ne '@index' and defined($c)) {
      0        
982 0         0 die 'invalid reverse property';
983             }
984 0         0 $definition->{'container_mapping'} = [$c];
985             }
986            
987 0 0       0 println "14.6" if $debug;
988 0         0 $definition->{'reverse'} = 1; # 14.6
989            
990             # 14.7
991 0 0       0 println "14.7 [setting defined{$term} = 1]" if $debug;
992 0         0 $activeCtx->{'terms'}{$term} = $definition;
993 0         0 $defined->{$term} = 1;
994 0         0 local($Data::Dumper::Indent) = 0;
995 0 0       0 println "returning from _4_2_2_create_term_definition: " . Dumper($activeCtx->{'terms'}{$term}) if $debug;
996 0         0 return;
997             }
998              
999 0 0       0 println "15" if $debug;
1000 0         0 $definition->{'reverse'} = 0; # 15
1001            
1002 0 0 0     0 if (exists $value->{'@id'} and (not(defined($value->{'@id'})) or $value->{'@id'} ne $term)) {
    0 0        
    0          
    0          
1003             # 16
1004 0 0       0 println "16" if $debug;
1005 0 0       0 if (not defined($value->{'@id'})) {
1006 0 0       0 println "16.1" if $debug;
1007             } else {
1008 0 0       0 println "16.2" if $debug;
1009 0         0 my $id = $value->{'@id'};
1010 0 0       0 if (not _is_string($id)) {
1011 0 0       0 println "16.2.1" if $debug;
1012 0         0 die 'invalid IRI mapping'; # 16.2
1013             }
1014            
1015 0 0 0     0 if (defined($id) and not exists $keywords{$id} and $id =~ /^@[A-Za-z]+$/) {
      0        
1016 0 0       0 println "16.2.2" if $debug;
1017 0         0 warn "create term definition encountered an \@id that looks like a keyword: $id\n";
1018 0         0 return;
1019             } else {
1020 0 0       0 println "16.2.3" if $debug;
1021 0         0 my $iri = $self->_5_2_2_iri_expansion($activeCtx, $id, vocab => 1, localCtx => $localCtx, 'defined' => $defined);
1022 0 0 0     0 if (not exists $keywords{$iri} and not $self->_is_abs_iri($iri) and $iri !~ /:/) {
      0        
1023 0         0 die 'invalid IRI mapping';
1024             }
1025 0 0       0 if ($iri eq '@context') {
1026 0         0 die 'invalid keyword alias';
1027             }
1028 0         0 $definition->{'iri_mapping'} = $iri;
1029             }
1030 0 0 0     0 if ($term =~ /.:./ or index($term, '/') >= 0) {
1031 0 0       0 println "16.2.4" if $debug;
1032 0 0       0 println "16.2.4.1" if $debug;
1033 0         0 $defined->{$term} = 1;
1034              
1035 0         0 my $iri = $self->_5_2_2_iri_expansion($activeCtx, $term, vocab => 1, localCtx => $localCtx, 'defined' => $defined);
1036 0 0       0 if ($iri ne $definition->{'iri_mapping'}) {
1037 0 0       0 println "16.2.4.2" if $debug;
1038 0         0 die 'invalid IRI mapping'; # 16.5 ; NOTE: the text here doesn't discuss what parameters to pass to IRI expansion
1039             }
1040             }
1041            
1042 0 0 0     0 if ($term !~ m{[:/]} and $simple_term and $definition->{'iri_mapping'} =~ m{[][:/?#@]$}) {
      0        
1043 0 0       0 println "16.2.5" if $debug;
1044 0         0 $definition->{'prefix_flag'} = 1;
1045             }
1046             # }
1047             }
1048             } elsif ($term =~ /.:/) {
1049             # 17
1050 0 0       0 println "17" if $debug;
1051 0         0 my ($prefix, $suffix) = split(/:/, $term, 2);
1052 0 0       0 if (exists $localCtx->{$prefix}) {
1053 0 0       0 println "17.1" if $debug;
1054 0         0 $self->_4_2_2_create_term_definition($activeCtx, $localCtx, $prefix, $defined); # 17.1
1055             }
1056 0 0       0 if (exists $activeCtx->{'terms'}{$prefix}) {
1057 0 0       0 println "17.2" if $debug;
1058 0         0 $definition->{'iri_mapping'} = $activeCtx->{'terms'}{$prefix}{'iri_mapping'} . $suffix; # 17.2
1059             } else {
1060 0 0       0 println "17.3" if $debug;
1061 0         0 $definition->{'iri_mapping'} = $term; # 17.3
1062             }
1063             } elsif ($term =~ m{/}) {
1064 0 0       0 println "18" if $debug;
1065 0         0 println "18.1"; # if $debug;
1066            
1067 0 0       0 println "18.2" if $debug;
1068 0         0 $definition->{'iri_mapping'} = $self->_5_2_2_iri_expansion($activeCtx, $term, vocab => 1);
1069 0 0       0 unless ($self->_is_iri($definition->{'iri_mapping'})) {
1070 0         0 println "18.2 invalid IRI: " . $definition->{'iri_mapping'};
1071 0         0 die 'invalid IRI mapping';
1072             }
1073             } elsif ($term eq '@type') {
1074 0 0       0 println "19" if $debug;
1075 0         0 $definition->{'iri_mapping'} = '@type'; # 19
1076             } else {
1077             # 20 ; NOTE: this section uses a passive voice "the IRI mapping of definition is set to ..." cf. 18 where it's active: "set the IRI mapping of definition to @type"
1078 0 0       0 if (exists $activeCtx->{'@vocab'}) {
1079 0 0       0 println "20" if $debug;
1080 0         0 $definition->{'iri_mapping'} = $activeCtx->{'@vocab'} . $term;
1081             } else {
1082 0 0       0 println "20" if $debug;
1083 0         0 die 'invalid IRI mapping';
1084             }
1085             }
1086            
1087 0 0       0 if (exists $value->{'@container'}) {
1088             # TODO: 21
1089 0 0       0 println "21" if $debug;
1090              
1091 0 0       0 println "21.1" if $debug;
1092 0         0 my $container = $value->{'@container'}; # 21.1
1093              
1094             # 21.1 error checking
1095 0         0 my %acceptable = map { $_ => 1 } qw(@graph @id @index @language @list @set @type);
  0         0  
1096 0 0       0 if (exists $acceptable{$container}) {
    0          
1097             } elsif (ref($container) eq 'ARRAY') {
1098 0 0 0     0 if (scalar(@$container) == 1) {
    0          
    0          
1099 0         0 my ($c) = @$container;
1100 0 0       0 unless (exists $acceptable{$c}) {
1101 0 0       0 println "21.1(a)" if $debug;
1102 0         0 die 'invalid container mapping';
1103             }
1104 0     0   0 } elsif (any { $_ =~ /^[@](id|index)$/ } @$container) { # any { $_ eq '@graph' } @$container
1105            
1106 0     0   0 } elsif (any { $_ eq '@set' } @$container and any { $_ =~ /^[@](index|graph|id|type|language)$/ } @$container) {
  0         0  
1107            
1108             } else {
1109 0 0       0 println "21.1(b)" if $debug;
1110 0         0 die 'invalid container mapping';
1111             }
1112             } else {
1113 0 0       0 println "21.1(c)" if $debug;
1114 0         0 die 'invalid container mapping';
1115             }
1116            
1117 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
1118 0 0 0 0   0 if (any { $container eq $_ } qw(@graph @id @type) or ref($container)) {
  0         0  
1119 0 0       0 println "21.2" if $debug;
1120 0         0 die 'invalid container mapping';
1121             }
1122             }
1123            
1124 0 0       0 println "21.3" if $debug;
1125 0 0       0 if (ref($container) eq 'ARRAY') {
1126 0         0 $definition->{'container_mapping'} = $container; # 21.3
1127             } else {
1128 0         0 $definition->{'container_mapping'} = [$container]; # 21.3
1129             }
1130            
1131 0 0       0 if ($container eq '@type') {
1132 0 0       0 println "21.4" if $debug;
1133 0 0       0 if (not defined($definition->{'type_mapping'})) {
1134 0 0       0 println "21.4.1" if $debug;
1135 0         0 $definition->{'type_mapping'} = '@id';
1136             }
1137            
1138 0         0 my $tm = $definition->{'type_mapping'};
1139 0 0 0     0 if ($tm ne '@id' and $tm ne '@vocab') {
1140 0 0       0 println "21.4.2" if $debug;
1141 0         0 die 'invalid type mapping';
1142             }
1143             }
1144             }
1145              
1146 0 0       0 if (exists $value->{'@index'}) {
1147 0 0       0 println "22" if $debug;
1148 0         0 my $container_mapping = $definition->{'container_mapping'};
1149 0 0 0     0 if ($self->processing_mode eq 'json-ld-1.0' or not $self->_cm_contains($container_mapping, '@index')) {
1150 0 0       0 println "22.1" if $debug;
1151 0         0 die 'invalid term definition';
1152             }
1153              
1154 0 0       0 println "22.2" if $debug;
1155 0         0 my $index = $value->{'@index'};
1156 0         0 my $expanded = $self->_5_2_2_iri_expansion($activeCtx, $index, vocab => 1);
1157 0 0       0 unless ($self->_is_abs_iri($expanded)) {
1158 0         0 die 'invalid term definition';
1159             }
1160            
1161 0 0       0 println "22.3" if $debug;
1162 0         0 $definition->{'index_mapping'} = $index;
1163             }
1164              
1165 0 0       0 if (exists $value->{'@context'}) {
1166 0 0       0 println "23" if $debug;
1167 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
1168 0 0       0 println "23.1" if $debug;
1169 0         0 die 'invalid term definition';
1170             }
1171              
1172 0 0       0 println "23.2" if $debug;
1173 0         0 my $context = $value->{'@context'};
1174              
1175 0 0       0 println "23.3" if $debug;
1176             # $self->_4_1_2_ctx_processing($activeCtx, $context, override_protected => 1, remote_contexts => clone($remote_contexts), validate_scoped_context => $validate_scoped_context, base_iri => $base_iri); # discard result
1177 0         0 $self->_4_1_2_ctx_processing($activeCtx, $context, override_protected => 1, remote_contexts => clone($remote_contexts), validate_scoped_context => 0, base_iri => $base_iri); # discard result
1178             # $self->_4_1_2_ctx_processing($activeCtx, $context, override_protected => 1); # discard result
1179            
1180 0         0 $definition->{'@context'} = $context; # Note: not sure about the spec text wording here: "Set the local context of definition to context." What is the "local context" of a definition?
1181             }
1182              
1183 0 0 0     0 if (exists $value->{'@language'} and not exists $value->{'@type'}) {
1184 0 0       0 println "24" if $debug;
1185 0 0       0 println "24.1" if $debug;
1186 0         0 my $language = $value->{'@language'};
1187 0 0 0     0 if (defined($language) and ref($language)) {
1188 0         0 die 'invalid language mapping';
1189             }
1190             # TODO: validate language tag against BCP47
1191              
1192 0 0       0 println "24.2" if $debug;
1193             # TODO: normalize language tag
1194 0         0 $definition->{'language_mapping'} = $language;
1195             }
1196              
1197 0 0 0     0 if (exists $value->{'@direction'} and not exists $value->{'@type'}) {
1198 0 0       0 println "25" if $debug;
1199 0         0 my $direction = $value->{'@direction'};
1200              
1201 0 0       0 println "25.1" if $debug;
1202 0 0 0     0 if (not(defined($direction))) {
    0          
1203             } elsif ($direction ne 'ltr' and $direction ne 'rtl') {
1204 0         0 die 'invalid base direction';
1205             }
1206            
1207 0         0 $definition->{'direction_mapping'} = $direction;
1208             }
1209              
1210 0 0       0 if (exists $value->{'@nest'}) {
1211 0 0       0 println "26" if $debug;
1212 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
1213 0 0       0 println "26.1" if $debug;
1214 0         0 die 'invalid term definition';
1215             }
1216            
1217 0 0       0 println "26.2" if $debug;
1218 0         0 my $nv = $value->{'@nest'};
1219 0 0 0     0 if (not(defined($nv)) or ref($nv)) {
    0 0        
1220 0         0 die 'invalid @nest value';
1221             } elsif (exists $keywords{$nv} and $nv ne '@nest') {
1222 0         0 die 'invalid @nest value';
1223             }
1224 0         0 $definition->{'nest_value'} = $nv;
1225             }
1226              
1227 0 0       0 if (exists $value->{'@prefix'}) {
1228 0 0       0 println "27" if $debug;
1229 0 0 0     0 if ($self->processing_mode eq 'json-ld-1.0' or $term =~ m{[:/]}) {
1230 0 0       0 println "27.1" if $debug;
1231 0         0 die 'invalid term definition'; # 27.1
1232             }
1233            
1234 0 0       0 println "27.2" if $debug;
1235 0         0 $definition->{'prefix_flag'} = $value->{'@prefix'};
1236 0 0       0 unless (JSON::is_bool($value->{'@prefix'})) {
1237 0         0 die 'invalid @prefix value';
1238             }
1239            
1240 0 0 0     0 if ($definition->{'prefix_flag'} and exists $keywords{$definition->{'iri_mapping'}}) {
1241 0 0       0 println "27.3" if $debug;
1242 0         0 die 'invalid term definition';
1243             }
1244             }
1245              
1246 0         0 my @keys = grep { not m/^[@](id|reverse|container|context|language|nest|prefix|type|direction|protected|index)$/ } keys %$value;
  0         0  
1247 0 0       0 if (scalar(@keys)) {
1248 0 0       0 println "28 " . Data::Dumper->Dump([\@keys, $value], ['invalid_keys', 'value']) if $debug;
1249 0         0 die 'invalid term definition'; # 28
1250             }
1251            
1252 0 0 0     0 if (not($override_protected) and $previous_defn->{'protected'}) {
1253             # 29
1254 0 0       0 println "29" if $debug;
1255 0         0 my %cmp_a = map { $_ => $definition->{$_} } grep { $_ ne 'protected' } keys %$definition;
  0         0  
  0         0  
1256 0         0 my %cmp_b = map { $_ => $previous_defn->{$_} } grep { $_ ne 'protected' } keys %$previous_defn;
  0         0  
  0         0  
1257 0         0 my $j = JSON->new->canonical(1);
1258 0 0       0 if ($j->encode(\%cmp_a) ne $j->encode(\%cmp_b)) {
1259 0 0       0 println "29.1" if $debug;
1260 0         0 die 'protected term redefinition'; # 29.1
1261             }
1262 0 0       0 println "29.2" if $debug;
1263 0         0 $definition = $previous_defn; # 29.2
1264             }
1265            
1266 0 0       0 println "30 [setting defined{$term} = 1]" if $debug;
1267 0 0       0 println "30 setting term definition " . Data::Dumper->Dump([$definition], [$term]) if $debug;
1268 0         0 $activeCtx->{'terms'}{$term} = $definition; # 30
1269 0         0 $defined->{$term} = 1; # 30
1270 0         0 local($Data::Dumper::Indent) = 0;
1271 0 0       0 println "returning from _4_2_2_create_term_definition: " . Dumper($activeCtx->{'terms'}{$term}) if $debug;
1272 0         0 return;
1273             }
1274              
1275             sub _4_3_inverse_context_creation {
1276 0     0   0 my $self = shift;
1277 0         0 my $activeCtx = shift;
1278             {
1279 2     2   33 no warnings 'uninitialized';
  2         6  
  2         4052  
  0         0  
1280 0 0       0 println "ENTER =================> _4_3_inverse_context_creation" if $debug;
1281             }
1282 0         0 my $__indent = indent();
1283 0 0       0 println "1" if $debug;
1284 0         0 my $result = {};
1285            
1286 0 0       0 println "2" if $debug;
1287 0         0 my $default_language = '@none';
1288 0 0       0 if (exists $activeCtx->{'@language'}) {
1289 0         0 $default_language = lc($activeCtx->{'@language'});
1290             }
1291            
1292 0 0       0 println "3" if $debug;
1293 0 0       0 foreach my $term (sort { length($a) <=> length($b) || $a cmp $b } keys %{ $activeCtx->{'terms'} }) {
  0         0  
  0         0  
1294 0         0 my $__indent = indent();
1295 0 0       0 println "3 [$term]" if $debug;
1296 0         0 my $tdef = $self->_ctx_term_defn($activeCtx, $term);
1297 0 0       0 if (not defined($tdef)) {
1298 0 0       0 println "3.1" if $debug;
1299 0         0 next;
1300             }
1301            
1302 0 0       0 println "3.2" if $debug;
1303 0         0 my $container = '@none';
1304 0   0     0 my $container_mapping = $tdef->{'container_mapping'} || [];
1305 0 0       0 if (scalar(@$container_mapping)) {
1306 0         0 $container = join('', sort @$container_mapping);
1307             }
1308            
1309 0 0       0 println "3.3" if $debug;
1310 0         0 my $var = $tdef->{'iri_mapping'};
1311            
1312 0 0       0 if (not exists $result->{$var}) {
1313 0 0       0 println "3.4" if $debug;
1314 0         0 $result->{$var} = {};
1315             }
1316            
1317 0 0       0 println "3.5" if $debug;
1318 0         0 my $container_map = $result->{$var};
1319            
1320 0 0       0 if (not exists $container_map->{$container}) {
1321 0 0       0 println "3.6" if $debug;
1322 0         0 $container_map->{$container} = {
1323             '@language' => {},
1324             '@type' => {},
1325             '@any' => {
1326             '@none' => $term
1327             }
1328             };
1329             }
1330            
1331 0 0       0 println "3.7" if $debug;
1332 0         0 my $type_language_map = $container_map->{$container};
1333            
1334 0 0       0 println "3.8" if $debug;
1335 0         0 my $type_map = $type_language_map->{'@type'};
1336            
1337 0 0 0     0 if ($tdef->{'reverse'}) {
    0          
    0          
1338 0 0       0 println "3.9" if $debug;
1339 0 0       0 if (not exists $type_map->{'@reverse'}) {
1340 0 0       0 println "3.9.1" if $debug;
1341 0         0 $type_map->{'@reverse'} = $term;
1342             }
1343             } elsif (($tdef->{'type_mapping'} // '') eq '@none') {
1344 0 0       0 println "3.10" if $debug;
1345 0 0       0 println "3.10.1" if $debug;
1346 0         0 my $language_map = $type_language_map->{'@language'};
1347            
1348 0 0       0 if (not exists $language_map->{'@any'}) {
1349 0 0       0 println "3.10.2" if $debug;
1350 0         0 $language_map->{'@any'} = $term;
1351             }
1352            
1353 0 0       0 if (not exists $type_map->{'@any'}) {
1354 0 0       0 println "3.10.3" if $debug;
1355 0         0 $type_map->{'@any'} = $term;
1356             }
1357             } elsif (exists $tdef->{'type_mapping'}) {
1358 0 0       0 println "3.11" if $debug;
1359 0 0       0 if (not exists $type_map->{$tdef->{'type_mapping'}}) {
1360 0 0       0 println "3.11.1" if $debug;
1361 0         0 $type_map->{$tdef->{'type_mapping'}} = $term;
1362             }
1363             }
1364            
1365 0 0       0 println "3.12" if $debug;
1366 0         0 my $language_map = $type_language_map->{'@language'};
1367            
1368 0         0 my $lang_dir; # TODO: the spec says to create this in 3.13.1, but it's used at this level
1369 0 0 0     0 if (exists $tdef->{'language_mapping'} and exists $tdef->{'direction_mapping'}) {
    0          
    0          
    0          
1370 0 0       0 println "3.13" if $debug;
1371 0 0       0 println "3.13.1" if $debug;
1372             # my $lang_dir;
1373              
1374 0 0 0     0 if (defined($tdef->{'language_mapping'}) and defined($tdef->{'direction_mapping'})) {
    0          
    0          
1375 0 0       0 println "3.13.2" if $debug;
1376 0         0 $lang_dir = lc(join('_', @{ $tdef }{qw(language_mapping direction_mapping)}));
  0         0  
1377             } elsif (defined($tdef->{'language_mapping'})) {
1378 0 0       0 println "3.13.3" if $debug;
1379 0         0 $lang_dir = lc($tdef->{'language_mapping'});
1380             } elsif (defined($tdef->{'direction_mapping'})) {
1381 0 0       0 println "3.13.4" if $debug;
1382 0         0 $lang_dir = '_' . lc($tdef->{'direction_mapping'});
1383             } else {
1384 0 0       0 println "3.13.5" if $debug;
1385 0         0 $lang_dir = '@null';
1386             }
1387 0 0       0 if (not exists $language_map->{$lang_dir}) {
1388 0 0       0 println "3.13.6" if $debug;
1389 0         0 $language_map->{$lang_dir} = $term;
1390             }
1391             } elsif (exists $tdef->{'language_mapping'}) {
1392 0 0       0 println "3.14" if $debug;
1393 0 0       0 println "3.14.1" if $debug;
1394 0 0       0 my $language = (not defined($tdef->{'language_mapping'})) ? '@null' : lc($tdef->{'language_mapping'});
1395              
1396 0 0       0 if (not exists $language_map->{$language}) {
1397 0 0       0 println "3.14.2" if $debug;
1398 0         0 $language_map->{$language} = $term;
1399             }
1400             } elsif (exists $tdef->{'direction_mapping'}) {
1401 0 0       0 println "3.15" if $debug;
1402 0 0       0 println "3.15.1" if $debug;
1403 0 0       0 my $direction = (not defined $tdef->{'direction_mapping'}) ? '@none' : '_' . $tdef->{'direction_mapping'};
1404              
1405 0 0       0 if (not exists $language_map->{$direction}) {
1406 0 0       0 println "3.15.2" if $debug;
1407 0         0 $language_map->{$direction} = $term;
1408             }
1409             } elsif (exists $activeCtx->{'@direction'}) {
1410 0 0       0 println "3.16" if $debug;
1411 0 0       0 println "3.16.1" if $debug;
1412 0         0 my $lang_dir = lc(join('_', $self->default_language, $self->default_base_direction));
1413            
1414 0 0       0 if (not exists $language_map->{$lang_dir}) {
1415 0 0       0 println "3.16.2" if $debug;
1416 0         0 $language_map->{$lang_dir} = $term;
1417             }
1418            
1419 0 0       0 if (not exists $language_map->{'@none'}) {
1420 0 0       0 println "3.16.3" if $debug;
1421 0         0 $language_map->{'@none'} = $term;
1422             }
1423            
1424 0 0       0 if (not exists $type_map->{'@none'}) {
1425 0 0       0 println "3.16.4" if $debug;
1426 0         0 $type_map->{'@none'} = $term;
1427             }
1428             } else {
1429 0 0       0 println "3.17" if $debug;
1430 0 0       0 if (not exists $language_map->{lc $default_language}) {
1431 0 0       0 println "3.17.1" if $debug;
1432 0         0 $language_map->{lc $default_language} = $term;
1433             }
1434            
1435 0 0       0 if (not exists $language_map->{'@none'}) {
1436 0 0       0 println "3.17.2" if $debug;
1437 0         0 $language_map->{'@none'} = $term;
1438             }
1439              
1440 0 0       0 if (not exists $type_map->{'@none'}) {
1441 0 0       0 println "3.17.3" if $debug;
1442 0         0 $type_map->{'@none'} = $term;
1443             }
1444             }
1445             }
1446              
1447 0 0       0 println "4" if $debug;
1448 0         0 return $result;
1449             }
1450              
1451             sub _4_4_2_term_selection {
1452 0     0   0 my $self = shift;
1453 0         0 my $inverseCtx = shift;
1454 0         0 my $var = shift;
1455 0         0 my $containers = shift;
1456 0         0 my $type_language = shift;
1457 0         0 my $preferred_values = shift;
1458 0 0       0 println "ENTER =================> _4_4_2_term_selection" if $debug;
1459 0         0 my $__indent = indent();
1460 0 0       0 println "1" if $debug;
1461 0         0 my $container_map = $inverseCtx->{$var};
1462            
1463 0 0       0 println "2" if $debug;
1464 0         0 foreach my $container (@$containers) {
1465 0 0       0 println "2 [$container]" if $debug;
1466 0 0       0 if (not exists $container_map->{$container}) {
1467 0 0       0 println "2.1" if $debug;
1468 0         0 next;
1469             }
1470 0 0       0 println "2.2" if $debug;
1471 0         0 my $type_language_map = $container_map->{$container};
1472            
1473 0 0       0 println "2.3" if $debug;
1474 0         0 my $value_map = $type_language_map->{$type_language};
1475            
1476 0 0       0 println "2.4" if $debug;
1477 0         0 foreach my $item (@$preferred_values) {
1478 0 0       0 if (not exists $value_map->{$item}) {
1479 0 0       0 println "2.4.1" if $debug;
1480 0         0 next;
1481             }
1482            
1483 0 0       0 println "2.4.2" if $debug;
1484 0         0 return $value_map->{$item};
1485             }
1486             }
1487              
1488 0 0       0 println "3" if $debug;
1489 0         0 return;
1490             }
1491              
1492             sub _5_1_2_expansion {
1493 2     2   4 my $self = shift;
1494 2         4 my $activeCtx = shift;
1495 2         11 my $activeProp = shift;
1496 2         5 my $element = shift;
1497             {
1498 2     2   21 no warnings 'uninitialized';
  2         5  
  2         2463  
  2         3  
1499 2 50       8 println "ENTER =================> _5_1_2_expansion('$activeProp')" if $debug;
1500             }
1501 2         11 my $__indent = indent();
1502 2         9 local($Data::Dumper::Indent) = 0;
1503 2 50       6 println(Data::Dumper->Dump([$activeCtx], ['activeCtx'])) if $debug;
1504 2 50       14 println(Data::Dumper->Dump([$activeProp], ['activeProp'])) if $debug;
1505 2 50       4 println(Data::Dumper->Dump([$element], ['element'])) if $debug;
1506 2         9 my %args = @_;
1507 2   100     9 my $frameExpansion = $args{frameExpansion} // 0;
1508 2   100     7 my $ordered = $args{ordered} // 0;
1509 2   50     8 my $fromMap = $args{fromMap} // 0;
1510            
1511 2 50       6 unless (defined($element)) {
1512 0 0       0 println "1 returning from _5_1_2_expansion: undefined element" if $debug;
1513 0         0 return; # 1
1514             }
1515 2 50 66     8 if (defined($activeProp) and $activeProp eq '@default') {
1516 0 0       0 println "2" if $debug;
1517 0         0 $frameExpansion = 0; # 2
1518             }
1519            
1520 2         4 my $property_scoped_ctx;
1521 2         5 my $property_scoped_ctx_defined = 0;
1522 2         18 my $tdef = $self->_ctx_term_defn($activeCtx, $activeProp);
1523 2 0 33     5 if ($tdef and exists $tdef->{'@context'}) {
1524 0         0 $property_scoped_ctx_defined = 1;
1525 0         0 my $lctx = $tdef->{'@context'};
1526 0         0 $property_scoped_ctx = $lctx; # 3
1527 0 0       0 println "3 property-scoped context for property $activeProp: " . Data::Dumper->Dump([$property_scoped_ctx], [qw(context)]) if $debug;
1528             }
1529            
1530 2 100       5 if (_is_scalar($element)) {
1531             # 4
1532 1 50       6 println "4" if $debug;
1533 1 50 33     5 if (not(defined($activeProp)) or $activeProp eq '@graph') {
1534 0 0       0 println "4.1 returning from _5_1_2_expansion: free floating scalar" if $debug;
1535 0         0 return; # 4.1
1536             }
1537 1 50       3 if ($property_scoped_ctx_defined) {
1538 0 0       0 println "4.2" if $debug;
1539 0         0 $activeCtx = $self->_4_1_2_ctx_processing($activeCtx, $property_scoped_ctx); # 4.2
1540 0 0       0 println "after 4.2: " . Data::Dumper->Dump([$activeCtx], ['activeCtx']) if $debug;
1541             }
1542            
1543 1 50       4 println "4.3" if $debug;
1544 1         6 my $v = $self->_5_3_2_value_expand($activeCtx, $activeProp, $element);
1545 1         4 local($Data::Dumper::Indent) = 1;
1546 1 50       3 println "4.3 returning from _5_1_2_expansion with " . Data::Dumper->Dump([$v], ['expandedValue']) if $debug;
1547 1         4 return $v; # 4.3
1548             }
1549            
1550 1 50       17 if (ref($element) eq 'ARRAY') {
1551             # 5
1552 0 0       0 println "5" if $debug;
1553 0         0 my @result; # 5.1
1554 0 0       0 println "5.1" if $debug;
1555 0         0 foreach my $item (@$element) {
1556             # 5.2
1557 0 0       0 println "5.2" if $debug;
1558 0 0       0 println "5.2.1" if $debug;
1559 0         0 my $expandedItem = $self->_5_1_2_expansion($activeCtx, $activeProp, $item, fromMap => $fromMap); # 5.2.1
1560 0 0       0 println "5.2.1 expanded item = " . Dumper($expandedItem) if $debug;
1561            
1562             # NOTE: 5.2.2 "container mapping" is in the term definition for active property, right? The text omits the term definition reference.
1563 0         0 my $container_mapping = $tdef->{'container_mapping'};
1564             # if (any { $_ eq '@list'} @$container_mapping and ref($expandedItem) eq 'ARRAY') {
1565 0 0 0     0 if ($self->_cm_contains($container_mapping, '@list') and ref($expandedItem) eq 'ARRAY') {
1566 0 0       0 println "5.2.2" if $debug;
1567 0         0 $expandedItem = { '@list' => $expandedItem }; # 5.2.2
1568             }
1569            
1570             # 5.2.3
1571 0 0       0 println "5.2.3" if $debug;
1572 0 0       0 if (ref($expandedItem) eq 'ARRAY') {
    0          
1573 0         0 push(@result, @$expandedItem);
1574             } elsif (defined($expandedItem)) {
1575 0         0 push(@result, $expandedItem);
1576             }
1577             }
1578            
1579 0         0 local($Data::Dumper::Indent) = 1;
1580 0 0       0 println "5.3 returning from _5_1_2_expansion with " . Data::Dumper->Dump([\@result], ['expanded_array_value']) if $debug;
1581 0         0 return \@result; # 5.3
1582             }
1583            
1584 1 50       7 unless (ref($element) eq 'HASH') {
1585 0 0       0 println "6" if $debug;
1586 0         0 die "Unexpected non-map encountered during expansion: $element";
1587             }
1588              
1589 1 50       5 if (my $prevCtx = $activeCtx->{'previous_context'}) {
1590 0 0       0 unless ($fromMap) {
1591 0         0 my @keys = keys %$element;
1592 0         0 my %expandedKeys = map { $_ => 1 } map { $self->_5_2_2_iri_expansion($activeCtx, $_, vocab => 1) } @keys;
  0         0  
  0         0  
1593 0 0       0 unless (exists $expandedKeys{'@value'}) {
1594 0 0 0     0 unless (scalar(@keys) == 1 and $self->_5_2_2_iri_expansion($activeCtx, $keys[0], vocab => 1) eq '@id') {
1595 0 0       0 println "7" if $debug;
1596 0         0 $activeCtx = $prevCtx; # 7
1597             }
1598             }
1599             }
1600             }
1601            
1602 1 50       4 if ($property_scoped_ctx_defined) {
1603 0 0       0 println "8" if $debug;
1604 0         0 my %args;
1605 0 0 0     0 if ($tdef and exists $tdef->{'__source_base_iri'}) {
1606 0         0 $args{base_iri} = $tdef->{'__source_base_iri'};
1607             }
1608 0         0 local($Data::Dumper::Indent) = 1;
1609 0 0       0 println "before 8: " . Data::Dumper->Dump([$activeCtx, $property_scoped_ctx], [qw'activeCtx property_scoped_ctx']) if $debug;
1610 0         0 $activeCtx = $self->_4_1_2_ctx_processing($activeCtx, $property_scoped_ctx, %args, override_protected => 1); # 8
1611 0 0       0 println "after 8: " . Data::Dumper->Dump([$activeCtx], ['activeCtx']) if $debug;
1612             }
1613            
1614 1 50       5 if (exists $element->{'@context'}) {
1615 1 50       5 println "9" if $debug;
1616 1         4 my $c = $element->{'@context'};
1617 1         5 $activeCtx = $self->_4_1_2_ctx_processing($activeCtx, $c); # 9
1618             }
1619            
1620 1 50       4 println "10" if $debug;
1621 1         10 my $type_scoped_ctx = clone($activeCtx); # 10
1622            
1623 1 50       5 println "11" if $debug;
1624 1         8 foreach my $key (sort keys %$element) {
1625 2         5 my $__indent = indent();
1626 2         4 my $value = $element->{$key};
1627             # 11
1628 2 50       4 println "11 [$key]" if $debug;
1629             {
1630 2     2   18 no warnings 'uninitialized';
  2         5  
  2         861  
  2         3  
1631 2 50       7 unless ('@type' eq $self->_5_2_2_iri_expansion($activeCtx, $key, vocab => 1)) {
1632 2 50       5 println "[skipping key $key in search of \@type]" if $debug;
1633 2         5 next;
1634             }
1635             }
1636              
1637 2 50       6 println "11 body [$key]" if $debug;
1638            
1639 2 50       6 unless (ref($value) eq 'ARRAY') {
1640 2 50       3 println "11.1 [$key]" if $debug;
1641 2         5 $value = [$value]; # 11.1
1642             }
1643            
1644 2         5 my %tdefs = map { $_ => $self->_ctx_term_defn($type_scoped_ctx, $_) } grep { _is_string($_) } @$value; # https://github.com/w3c/json-ld-api/issues/304
  1         4  
  2         5  
1645            
1646             # the clone here is necessary, because the implicit lexicographic sort will add the POK flag to integer scalars that would otherwise be just IOK. this causes the resulting JSON serialization to treat the scalar as a string.
1647 2         5 foreach my $term (sort @{clone($value)}) {
  2         17  
1648 2 50       7 println "11.2 attempting with [$term]" if $debug;
1649 2 100       16 if (_is_string($term)) {
1650 1         4 my $tdef = $tdefs{$term};
1651 1 50       8 if (exists $tdef->{'@context'}) {
1652 0 0       0 println "11.2" if $debug;
1653 0         0 my $c = $tdef->{'@context'};
1654 0         0 $activeCtx = $self->_4_1_2_ctx_processing($activeCtx, $c, propagate => 0);
1655 0         0 local($Data::Dumper::Indent) = 1;
1656 0 0       0 println "11.2 " . Data::Dumper->Dump([$activeCtx], ['activeCtx']) if $debug;
1657             }
1658             }
1659             }
1660            
1661             }
1662 1 50       3 println "After 11, " . Data::Dumper->Dump([$element], ['element']) if $debug;
1663            
1664 1 50       7 println "12" if $debug;
1665 1         4 my $result = {}; # 12a
1666 1         2 my $nests = {}; # 12b
1667 1         3 my $input_type = '';
1668 1         4 foreach my $key (sort keys %$element) {
1669 2         6 my $expandedKey = $self->_5_2_2_iri_expansion($activeCtx, $key);
1670 2     2   20 no warnings 'uninitialized';
  2         5  
  2         18970  
1671 2 50       11 if ($expandedKey eq '@type') {
1672 0         0 $input_type = $self->_5_2_2_iri_expansion($activeCtx, $element->{$key});
1673 0         0 last;
1674             }
1675             }
1676 1 50       5 println "12 " . Data::Dumper->Dump([$input_type], ['*input_type']) if $debug;
1677            
1678 1         6 $self->_5_1_2_expansion_step_13($activeCtx, $type_scoped_ctx, $result, $activeProp, $input_type, $nests, $ordered, $frameExpansion, $element);
1679 1 50       3 println "after 13,14: " . Data::Dumper->Dump([$result], ['*result']) if $debug;
1680              
1681 1 50 33     12 if (exists $result->{'@value'}) {
    50 33        
    50          
1682             # 15
1683 0 0       0 println "15" if $debug;
1684 0         0 my @keys = keys %$result;
1685 0         0 my %acceptable = map { $_ => 1 } qw(@direction @index @language @type @value);
  0         0  
1686 0         0 foreach my $k (@keys) {
1687 0 0       0 unless ($acceptable{$k}) {
1688 0 0       0 println "15.1 [$k]" if $debug;
1689 0         0 die 'invalid value object' ; # 15.1
1690             }
1691             }
1692 0 0 0     0 if (exists $result->{'@language'} or exists $result->{'@direction'}) {
1693 0 0       0 println "15.1 \@language handling" if $debug;
1694 0 0       0 die 'invalid value object' if (exists $result->{'@type'}); # 15.1
1695             }
1696            
1697             # if (not(defined($result->{'@value'}))) {
1698 0 0 0     0 if (defined($result->{'@type'}) and $result->{'@type'} eq '@json') {
    0 0        
    0 0        
    0 0        
      0        
1699 0 0       0 println "15.2" if $debug;
1700             # TODO: treat $result->{'@value'} as a JSON literal
1701 0         0 } elsif (not(defined($result->{'@value'})) or (ref($result->{'@value'}) eq 'ARRAY' and not scalar(@{$result->{'@value'}}))) { # based on irc conversation with gkellog
1702 0 0       0 println "15.3" if $debug;
1703 0         0 return undef;
1704             } elsif (ref($result->{'@value'}) and exists $result->{'@language'}) {
1705 0 0       0 println "15.4" if $debug;
1706 0         0 die 'invalid language-tagged value; ' . Dumper($result); # 15.4
1707             } elsif (exists $result->{'@type'} and not($self->_is_iri($result->{'@type'}))) {
1708 0         0 println "Not an IRI \@type: " . Dumper($result->{'@type'});
1709 0 0       0 println "15.5" if $debug;
1710 0         0 die 'invalid typed value: ' . Dumper($result); # 15.5
1711             # } elsif (exists $result->{'@type'}) {
1712             # my $types = $result->{'@type'};
1713             # my @types = (ref($types) eq 'ARRAY') ? @$types : $types;
1714             # foreach my $t (@types) {
1715             # unless ($self->_is_iri($t)) {
1716             # warn "Not an IRI \@type: " . Dumper($result->{'@type'});
1717             # println "15.5" if $debug;
1718             # die 'invalid typed value: ' . Dumper($result); # 15.5
1719             # }
1720             # }
1721             }
1722 0 0       0 println "15 resulting in " . Data::Dumper->Dump([$result], ['*result']) if $debug;
1723             } elsif (exists $result->{'@type'} and ref($result->{'@type'}) ne 'ARRAY') {
1724 0 0       0 println "16" if $debug;
1725 0         0 $result->{'@type'} = [$result->{'@type'}]; # 16
1726 0 0       0 println "16 resulting in " . Data::Dumper->Dump([$result], ['*result']) if $debug;
1727             } elsif (exists $result->{'@set'} or exists $result->{'@list'}) {
1728             # 17
1729 0 0       0 println "17" if $debug;
1730 0 0       0 my @keys = grep { $_ ne '@set' and $_ ne '@list' } keys %$result;
  0         0  
1731 0 0       0 if (scalar(@keys)) {
1732 0 0       0 println "17.1" if $debug;
1733 0 0 0     0 die 'invalid set or list object' unless (scalar(@keys) == 1 and $keys[0] eq '@index'); # 17.1
1734             }
1735 0 0       0 if (exists $result->{'@set'}) {
1736 0 0       0 println "17.2" if $debug;
1737 0         0 $result = $result->{'@set'}; # 17.2
1738             }
1739 0 0       0 println "17 resulting in " . Data::Dumper->Dump([$result], ['*result']) if $debug;
1740             }
1741            
1742 1 50       2 println "after 17 resulting in " . Data::Dumper->Dump([$result], ['*result']) if $debug;
1743 1 50       7 my @keys = (ref($result) eq 'HASH') ? keys %$result : ();
1744 1 50       3 if (ref($result) eq 'HASH') { # NOTE: assuming based on the effects of 16.2 that this condition is necessary to guard against cases where $result is not a hashref.
1745 1 50 33     15 if (scalar(@keys) == 1 and $keys[0] eq '@language') {
1746 0 0       0 println "18" if $debug;
1747 0         0 return undef;
1748             }
1749            
1750 1 50       6 println(Data::Dumper->Dump([$activeProp], ['activeProp'])) if $debug;
1751 1 50 33     4 if (not(defined($activeProp)) or $activeProp eq '@graph') {
1752             # 19
1753 1         2 local($Data::Dumper::Indent) = 0;
1754 1 50       3 println "19 " . Data::Dumper->Dump([$result, \@keys], ['*result', '*keys']) if $debug;
1755 1 50 33     72 if (ref($result) eq 'HASH' and (scalar(@keys) == 0 or exists $result->{'@value'} or exists $result->{'@list'})) {
    50 33        
      33        
      33        
1756 0 0       0 println "19.1" if $debug;
1757 0         0 $result = undef; # 19.1
1758             } elsif (ref($result) eq 'HASH' and scalar(@keys) == 1 and $keys[0] eq '@id') {
1759 0 0       0 unless ($frameExpansion) {
1760 0 0       0 println "19.2" if $debug;
1761 0         0 $result = undef; # 19.2
1762             }
1763             }
1764             }
1765 1 50       10 println "19 resulting in " . Data::Dumper->Dump([$result], ['*result']) if $debug;
1766             }
1767            
1768 1         61 local($Data::Dumper::Indent) = 1;
1769 1 50       7 println "20 returning from _5_1_2_expansion with final value " . Data::Dumper->Dump([$result], ['*result']) if $debug;
1770 1         7 return $result; # 19
1771             }
1772            
1773             sub _5_1_2_expansion_step_13 {
1774 1     1   3 my $self = shift;
1775 1         2 my $activeCtx = shift;
1776 1         3 my $type_scoped_ctx = shift;
1777 1         1 my $result = shift;
1778 1         2 my $activeProp = shift;
1779 1         2 my $input_type = shift;
1780 1         2 my $nests = shift;
1781 1         2 my $ordered = shift;
1782 1         1 my $frameExpansion = shift;
1783 1         12 my $element = shift;
1784 1 50       9 println "13 --- processing " . Data::Dumper->Dump([$element], ['element']) if ($debug);
1785 1         7 foreach my $key (sort keys %$element) {
1786 2         6 my $__indent = indent();
1787 2         6 my $value = $element->{$key};
1788             # 13
1789 2 50       5 println '-----------------------------------------------------------------' if $debug;
1790 2 50       4 println "13 [$key] " . Data::Dumper->Dump([$value], ['value']) if $debug;
1791 2 100       4 if ($key eq '@context') {
1792 1 50       3 println "13.1 going to next element key" if $debug;
1793 1         2 next; # 13.1
1794             }
1795 1         5 local($Data::Dumper::Indent) = 1;
1796 1 50       3 println(Data::Dumper->Dump([$activeCtx], ['activeCtx'])) if $debug;
1797            
1798 1 50       3 println "13.2" if $debug;
1799 1         4 my $expandedProperty = $self->_5_2_2_iri_expansion($activeCtx, $key, vocab => 1); # 13.2
1800 1 50       9 println "13.2 " . Data::Dumper->Dump([$expandedProperty], ['expandedProperty']) if $debug;
1801 1 50       4 println(Data::Dumper->Dump([$expandedProperty], ['expandedProperty'])) if $debug;
1802 1 0 33     59 if (not(defined($expandedProperty)) or ($expandedProperty !~ /:/ and not exists $keywords{$expandedProperty})) {
      33        
1803 0 0       0 println "13.3 going to next element key" if $debug;
1804 0         0 next; # 13.3
1805             }
1806            
1807 1         5 my $expandedValue;
1808 1 50       4 if (exists $keywords{$expandedProperty}) {
1809             # 13.4
1810 0 0       0 println "13.4 keyword: $expandedProperty" if $debug;
1811            
1812 0 0 0     0 if (defined($activeProp) and $activeProp eq '@reverse') {
1813 0 0       0 println "13.4.1" if $debug;
1814 0         0 die 'invalid reverse property map'; # 13.4.1
1815             }
1816              
1817 0 0       0 if (exists $result->{$expandedProperty}) {
1818 0         0 my $p = $result->{$expandedProperty};
1819 0 0 0     0 if ($expandedProperty ne '@included' and $expandedProperty ne '@type') {
1820 0 0       0 println "13.4.2 colliding: $expandedProperty" if $debug;
1821 0         0 die 'colliding keywords'; # 13.4.2
1822             }
1823             }
1824            
1825             # NOTE: another case of an "Otherwise" applying to a partial conjunction
1826 0 0       0 if ($expandedProperty eq '@id') {
1827 0 0       0 println "13.4.3" if $debug;
1828 0 0 0     0 if (ref($value) or not defined($value)) {
1829 0 0       0 println "13.4.3.1 invalid" if $debug;
1830 0         0 die 'invalid @id value';
1831             } else {
1832 0 0       0 println "13.4.3.2" if $debug;
1833 0         0 $expandedValue = $self->_5_2_2_iri_expansion($activeCtx, $value, documentRelative => 1);
1834 0 0       0 println "13.4.3.2 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1835             }
1836             }
1837              
1838 0 0       0 if ($expandedProperty eq '@type') {
1839 0 0       0 println "13.4.4" if $debug;
1840 0         0 my $is_string = _is_string($value);
1841 0         0 my $is_array = ref($value) eq 'ARRAY';
1842 0   0 0   0 my $is_array_of_strings = ($is_array and all { _is_string($_) } @$value);
  0         0  
1843 0 0 0     0 if (not($is_string) and not($is_array_of_strings)) {
1844 0 0       0 println "13.4.4.1 invalid" if $debug;
1845 0         0 die 'invalid type value';
1846             }
1847            
1848 0 0 0     0 if (ref($value) eq 'HASH' and scalar(%$value) == 0) {
    0          
1849 0 0       0 println "13.4.4.2" if $debug;
1850 0         0 $expandedValue = $value;
1851 0 0       0 println "13.4.4.2 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1852             } elsif ($self->_is_default_object($value)) {
1853 0 0       0 println "13.4.4.3" if $debug;
1854 0         0 $expandedValue = { '@default' => $self->_5_2_2_iri_expansion($type_scoped_ctx, $value, vocab => 1, documentRelative => 1) };
1855 0 0       0 println "13.4.4.3 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1856             } else {
1857 0 0       0 println "13.4.4.4" if $debug;
1858 0 0       0 if (ref($value)) {
1859 0         0 $expandedValue = [map {$self->_5_2_2_iri_expansion($type_scoped_ctx, $_, vocab => 1, documentRelative => 1)} @$value];
  0         0  
1860             } else {
1861 0         0 $expandedValue = $self->_5_2_2_iri_expansion($type_scoped_ctx, $value, vocab => 1, documentRelative => 1);
1862             }
1863 0 0       0 println "13.4.4.4 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1864             }
1865            
1866 0 0       0 if (my $t = $result->{'@type'}) {
1867 0 0       0 println "13.4.4.5" if $debug;
1868 0 0       0 if (ref($expandedValue) ne 'ARRAY') {
1869 0         0 $expandedValue = [$expandedValue];
1870             }
1871 0         0 unshift(@$expandedValue, $t);
1872 0 0       0 println "13.4.4.5 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1873             }
1874             }
1875              
1876 0 0       0 if ($expandedProperty eq '@graph') {
1877 0 0       0 println "13.4.5" if $debug;
1878 0         0 my $v = $self->_5_1_2_expansion($activeCtx, '@graph', $value, frameExpansion => $frameExpansion, ordered => $ordered);
1879 0 0       0 $expandedValue = (ref($v) eq 'ARRAY') ? $v : [$v];
1880 0 0       0 println("========================================================================") if $debug;
1881 0 0       0 println("========================================================================") if $debug;
1882 0 0       0 println("========================================================================") if $debug;
1883 0 0       0 println(Dumper($expandedValue)) if $debug;
1884 0 0       0 println "13.4.5 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1885             }
1886              
1887 0 0       0 if ($expandedProperty eq '@included') {
    0          
1888 0 0       0 println "13.4.6" if $debug;
1889 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
1890 0 0       0 println "13.4.6.1" if $debug;
1891 0         0 next;
1892             }
1893            
1894 0 0       0 println "13.4.6.2" if $debug;
1895 0         0 $expandedValue = $self->_5_1_2_expansion($activeCtx, $activeProp, $value, frameExpansion => $frameExpansion, ordered => $ordered);
1896 0 0       0 unless (ref($expandedValue) eq 'ARRAY') {
1897 0         0 $expandedValue = [$expandedValue];
1898             }
1899            
1900 0         0 foreach my $v (@$expandedValue) {
1901 0 0       0 unless ($self->_is_node_object($v)) {
1902 0 0       0 println "13.4.6.3" if $debug;
1903 0         0 die 'invalid @included value';
1904             }
1905             }
1906            
1907 0 0       0 if (exists $result->{'@included'}) {
1908             # https://github.com/w3c/json-ld-api/issues/333
1909 0 0       0 println "13.4.6.4" if $debug;
1910 0         0 unshift(@$expandedValue, @{ $result->{'@included'} });
  0         0  
1911             }
1912 0 0       0 println "13.4.6 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1913             } elsif ($expandedProperty eq '@value') {
1914 0 0       0 println "13.4.7 " . Data::Dumper->Dump([$input_type], ['*input_type']) if $debug;
1915 0 0 0     0 if ($input_type eq '@json') {
    0          
1916 0 0       0 println "13.4.7.1" if $debug;
1917 0         0 $expandedValue = $value; # 13.4.7.1
1918 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
1919 0         0 die 'invalid value object value';
1920             }
1921             } elsif (not (_is_scalar($value) or not defined($value))) { # "if value is not a scalar or null, an invalid value object value error has been detected"
1922 0 0       0 println "13.4.7.2 " . Data::Dumper->Dump([$value], ['*value']) if $debug; # NOTE: the language here is ambiguous: "if value is not a scalar or null"
1923 0         0 die 'invalid value object value';
1924             } else {
1925 0 0       0 println "13.4.7.3" if $debug;
1926 0         0 $expandedValue = $value;
1927             }
1928            
1929 0 0       0 unless (defined($expandedValue)) {
1930 0 0       0 println "13.4.7.4" if $debug;
1931 0         0 $result->{'@value'} = undef;
1932 0         0 next;
1933             }
1934 0 0       0 println "13.4.7 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1935             }
1936              
1937             # NOTE: again with the "Otherwise" that seems to apply to only half the conjunction
1938 0 0       0 if ($expandedProperty eq '@language') {
1939 0 0       0 println "13.4.8" if $debug;
1940 0 0       0 if (ref($value)) {
1941 0 0       0 println "13.4.8.1" if $debug;
1942 0 0       0 if ($frameExpansion) {
1943 0         0 println "13.4.8.1 TODO: frameExpansion support"; # if $debug;
1944             }
1945 0         0 die 'invalid language-tagged string';
1946             }
1947 0 0       0 println "13.4.8.2" if $debug;
1948 0         0 $expandedValue = $value; # 13.4.8.2
1949             # TODO: validate language tag against BCP47
1950 0 0       0 println "13.4.8 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1951             }
1952              
1953 0 0       0 if ($expandedProperty eq '@direction') {
1954 0 0       0 println "13.4.9" if $debug;
1955 0 0       0 if ($self->processing_mode eq 'json-ld-1.0') {
1956 0 0       0 println "13.4.9.1" if $debug;
1957 0         0 next;
1958             }
1959              
1960 0 0 0     0 if ($value ne 'ltr' and $value ne 'rtl') {
1961 0 0       0 println "13.4.9.2" if $debug;
1962 0         0 die 'invalid base direction';
1963             }
1964              
1965 0 0       0 println "13.4.9.3" if $debug;
1966 0         0 $expandedValue = $value;
1967              
1968 0 0       0 if ($frameExpansion) {
1969 0         0 println "13.4.9.4 TODO: frameExpansion support"; # if $debug;
1970             }
1971 0 0       0 println "13.4.9 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1972             }
1973              
1974 0 0       0 if ($expandedProperty eq '@index') {
1975 0 0       0 println "13.4.10" if $debug;
1976 0 0       0 if (ref($value)) {
1977 0 0       0 println "13.4.10.1" if $debug;
1978 0         0 die 'invalid @index value';
1979             }
1980            
1981 0 0       0 println "13.4.10.2" if $debug;
1982 0         0 $expandedValue = $value;
1983 0 0       0 println "13.4.10 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
1984             }
1985              
1986 0 0       0 if ($expandedProperty eq '@list') {
1987 0 0       0 println "13.4.11" if $debug;
1988 0 0 0     0 if (not defined($activeProp) or $activeProp eq '@graph') {
1989 0 0       0 println "13.4.11.1" if $debug;
1990 0         0 next;
1991             }
1992              
1993 0 0       0 println "13.4.11.2" if $debug;
1994 0         0 $expandedValue = $self->_5_1_2_expansion($activeCtx, $activeProp, $value, frameExpansion => $frameExpansion, ordered => $ordered);
1995 0 0       0 if (ref($expandedValue) ne 'ARRAY') {
1996             # https://github.com/w3c/json-ld-api/issues/310
1997 0         0 $expandedValue = [$expandedValue];
1998             }
1999 0 0       0 println "13.4.11 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2000             }
2001              
2002 0 0       0 if ($expandedProperty eq '@set') {
2003 0 0       0 println "13.4.12" if $debug;
2004 0         0 $expandedValue = $self->_5_1_2_expansion($activeCtx, $activeProp, $value, frameExpansion => $frameExpansion, ordered => $ordered);
2005 0 0       0 println "13.4.12 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2006             }
2007              
2008             # NOTE: the language here is really confusing. the first conditional in 13.4.13 is the conjunction "expanded property is @reverse and value is not a map".
2009             # however, by context it seems that really everything under 13.4.13 assumes expanded property is @reverse, and the first branch is dependent only on 'value is not a map'.
2010 0 0       0 if ($expandedProperty eq '@reverse') {
2011 0 0       0 println "13.4.13" if $debug;
2012 0 0       0 if (ref($value) ne 'HASH') {
2013 0 0       0 println "13.4.13.1" if $debug;
2014 0         0 die 'invalid @reverse value';
2015             }
2016              
2017 0 0       0 println "13.4.13.2" if $debug;
2018 0         0 $expandedValue = $self->_5_1_2_expansion($activeCtx, '@reverse', $value, frameExpansion => $frameExpansion, ordered => $ordered); # 13.4.13.1
2019 0 0       0 println "13.4.13.2 " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2020            
2021 0 0 0     0 if (ref($expandedValue) eq 'HASH' and exists $expandedValue->{'@reverse'}) { # NOTE: spec text does not assert that expandedValue is a map
2022 0 0       0 println "13.4.13.3" if $debug;
2023 0         0 foreach my $property (keys %{ $expandedValue->{'@reverse'} }) {
  0         0  
2024 0         0 my $__indent = indent();
2025 0 0       0 println "13.4.13.3 [$property]" if $debug;
2026 0         0 my $item = $expandedValue->{'@reverse'}{$property};
2027 0 0       0 if (not exists $result->{$property}) {
2028 0 0       0 println "13.4.13.3.1" if $debug;
2029 0         0 $result->{$property} = [];
2030             }
2031            
2032 0 0       0 println "13.4.13.3.2" if $debug;
2033 0         0 push(@{ $result->{$property} }, @$item);
  0         0  
2034             }
2035             }
2036            
2037 0 0       0 if (ref($expandedValue) eq 'HASH') { # NOTE: spec text does not assert that expandedValue is a map
2038 0         0 my @keys = grep { $_ ne '@reverse' } keys %$expandedValue;
  0         0  
2039 0 0       0 if (scalar(@keys)) {
2040 0 0       0 println "13.4.13.4" if $debug;
2041            
2042 0 0       0 if (not exists $result->{'@reverse'}) {
2043 0 0       0 println "13.4.13.4.1" if $debug;
2044 0         0 $result->{'@reverse'} = {};
2045             }
2046            
2047 0 0       0 println "13.4.13.4.2" if $debug;
2048 0         0 my $reverse_map = $result->{'@reverse'};
2049            
2050 0 0       0 println "13.4.13.4.3" if $debug;
2051 0         0 foreach my $property (grep { $_ ne '@reverse' } keys %{ $expandedValue }) {
  0         0  
  0         0  
2052 0         0 my $__indent = indent();
2053 0 0       0 println "13.4.13.4.3 [$property]" if $debug;
2054 0         0 my $items = $expandedValue->{$property};
2055            
2056 0 0       0 println "13.4.13.4.3.1" if $debug;
2057 0         0 foreach my $item (@$items) {
2058 0         0 my $__indent = indent();
2059 0 0 0     0 if ($self->_is_value_object($item) or $self->_is_list_object($item)) {
2060 0 0       0 println "13.4.13.4.3.1.1" if $debug;
2061 0         0 die 'invalid reverse property value';
2062             }
2063            
2064 0 0       0 if (not exists $reverse_map->{$property}) {
2065 0 0       0 println "13.4.13.4.3.1.2" if $debug;
2066 0         0 $reverse_map->{$property} = [];
2067             }
2068            
2069 0 0       0 println "13.4.13.4.3.1.3" if $debug;
2070 0         0 push(@{ $reverse_map->{$property} }, $item);
  0         0  
2071             }
2072             }
2073             }
2074             }
2075 0 0       0 println "13.4.13.5 going to next element key" if $debug;
2076 0         0 next; # 13.4.13.5
2077             }
2078              
2079 0 0       0 if ($expandedProperty eq '@nest') {
2080 0 0       0 println "13.4.14 adding '$key' to nests" if $debug;
2081 0   0     0 $nests->{$key} //= [];
2082 0         0 next;
2083             }
2084              
2085 0 0       0 if ($frameExpansion) {
2086 0         0 my %other_framings = map { $_ => 1 } qw(@explicit @default @embed @explicit @omitDefault @requireAll);
  0         0  
2087 0 0       0 if ($other_framings{$expandedProperty}) {
2088 0 0       0 println "13.4.15" if $debug;
2089 0         0 $expandedValue = $self->_5_1_2_expansion($activeCtx, $activeProp, $value, frameExpansion => $frameExpansion, ordered => $ordered); # 13.4.15
2090 0 0       0 println "13.4.15 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2091             }
2092             }
2093            
2094 0 0       0 println "before 13.4.16 " . Data::Dumper->Dump([$expandedValue, $expandedProperty, $input_type], [qw'expandedValue expandedProperty input_type']) if $debug;
2095 0 0 0     0 unless (not(defined($expandedValue)) and $expandedProperty eq '@value' and $input_type ne '@json') {
      0        
2096 0 0       0 println "13.4.16 setting " . Data::Dumper->Dump([$expandedValue], [$expandedProperty]) if $debug;
2097             # println "$expandedProperty expanded value is " . Dumper($expandedValue) if $debug;
2098 0         0 $result->{$expandedProperty} = $expandedValue; # https://github.com/w3c/json-ld-api/issues/270
2099 0 0       0 println "13.4.16 resulting in " . Data::Dumper->Dump([$result], ['*result']) if $debug;
2100             }
2101              
2102 0 0       0 println "13.4.17 going to next element key" if $debug;
2103 0         0 next; # 13.4.17
2104             }
2105              
2106              
2107 1         5 my $tdef = $self->_ctx_term_defn($activeCtx, $key);
2108              
2109 1 50       4 println "13.5 initializing container mapping" if $debug;
2110 1         3 my $container_mapping = $tdef->{'container_mapping'}; # 13.5
2111 1 50       3 println(Data::Dumper->Dump([$container_mapping, $value], ['*container_mapping', '*value'])) if $debug;
2112              
2113 1 50 33     8 if (exists($tdef->{'type_mapping'}) and $tdef->{'type_mapping'} eq '@json') {
    50 33        
    50 33        
2114 0 0       0 println "13.6" if $debug;
2115 0         0 $expandedValue = { '@value' => $value, '@type' => '@json' }; # 13.6
2116 0 0       0 println "13.6 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2117             } elsif ($self->_cm_contains($container_mapping, '@language') and ref($value) eq 'HASH') {
2118 0 0       0 println "13.7" if $debug;
2119 0 0       0 println "13.7.1" if $debug;
2120 0         0 $expandedValue = [];
2121            
2122 0 0       0 println "13.7.2" if $debug;
2123 0         0 my $direction = $activeCtx->{'@direction'};
2124            
2125 0 0       0 if (exists $tdef->{'direction_mapping'}) {
2126 0 0       0 println "13.7.3" if $debug;
2127 0         0 $direction = $tdef->{'direction_mapping'};
2128             }
2129            
2130 0 0       0 println "13.7.4" if $debug;
2131 0         0 for my $language (sort keys %$value) {
2132 0         0 my $__indent = indent();
2133 0         0 my $language_value = $value->{$language};
2134 0 0       0 println "13.7.4 [$language]" if $debug;
2135            
2136 0 0       0 if (ref($language_value) ne 'ARRAY') {
2137 0 0       0 println "13.7.4.1" if $debug;
2138 0         0 $language_value = [$language_value];
2139             }
2140            
2141 0 0       0 println "13.7.4.2" if $debug;
2142 0         0 foreach my $item (@$language_value) {
2143 0         0 my $__indent = indent();
2144 0 0       0 unless (defined($item)) {
2145 0 0       0 println "13.7.4.2.1" if $debug;
2146 0         0 next;
2147             }
2148            
2149 0 0       0 if (ref($item)) {
2150 0 0       0 println "13.7.4.2.2" if $debug;
2151 0         0 die 'invalid language map value';
2152             }
2153            
2154 0 0       0 println "13.7.4.2.3" if $debug;
2155 0         0 my $v = {'@value' => $item, '@language' => $language};
2156 0         0 my $well_formed = 1; # TODO: check BCP47 well-formedness of $item
2157 0 0 0     0 if ($item ne '@none' and not($well_formed)) {
2158 0         0 warn "Language tag is not well-formed: $item";
2159             }
2160             # TODO: normalize language tag
2161            
2162 0         0 my $expandedLanguage = $self->_5_2_2_iri_expansion($activeCtx, $language);
2163 0 0 0     0 if ($language eq '@none' or $expandedLanguage eq '@none') {
2164 0 0       0 println "13.7.4.2.4" if $debug;
2165 0         0 delete $v->{'@language'};
2166             }
2167            
2168 0 0       0 if (defined($direction)) {
2169 0 0       0 println "13.7.4.2.5" if $debug;
2170 0         0 $v->{'@direction'} = $direction;
2171             }
2172            
2173 0 0       0 println "13.7.4.2.6" if $debug;
2174 0         0 push(@$expandedValue, $v);
2175             }
2176             }
2177 0 0       0 println "13.7 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2178             # } elsif ((exists $container_mapping->{'@index'} or exists $container_mapping->{'@type'} or exists $container_mapping->{'@id'}) and ref($value) eq 'HASH') {
2179             } elsif ($self->_cm_contains_any($container_mapping, '@index', '@type', '@id') and ref($value) eq 'HASH') {
2180 0 0       0 println "13.8" if $debug;
2181 0 0       0 println "13.8.1" if $debug;
2182 0         0 $expandedValue = [];
2183            
2184 0 0       0 println "13.8.2" if $debug;
2185 0   0     0 my $index_key = $tdef->{'index_mapping'} // '@index';
2186            
2187 0 0       0 println "13.8.3" if $debug;
2188 0         0 foreach my $index (sort keys %$value) {
2189 0         0 my $__indent = indent();
2190 0         0 my $index_value = $value->{$index};
2191 0 0       0 println '-----------------------------------------------------------------' if $debug;
2192 0 0       0 println "13.8.3 [$index]" if $debug;
2193 0         0 my $map_context;
2194 0 0       0 if ($self->_cm_contains_any($container_mapping, '@id', '@type')) {
2195 0 0       0 println "13.8.3.1" if $debug;
2196 0   0     0 $map_context = $activeCtx->{'previous_context'} // $activeCtx;
2197             } else {
2198 0         0 $map_context = $activeCtx;
2199             }
2200            
2201 0         0 my $index_tdef = $self->_ctx_term_defn($map_context, $index);
2202 0 0 0     0 if ($self->_cm_contains_any($container_mapping, '@type') and exists $index_tdef->{'@context'}) {
2203 0 0       0 println "13.8.3.2" if $debug;
2204 0         0 $map_context = $self->_4_1_2_ctx_processing($map_context, $index_tdef->{'@context'});
2205             } else {
2206 0 0       0 println "13.8.3.3" if $debug;
2207 0         0 $map_context = $activeCtx;
2208             }
2209            
2210 0 0       0 println "13.8.3.4" if $debug;
2211 0         0 my $expanded_index = $self->_5_2_2_iri_expansion($activeCtx, $index, vocab => 1);
2212              
2213 0 0       0 if (ref($index_value) ne 'ARRAY') {
2214 0 0       0 println "13.8.3.5" if $debug;
2215 0         0 $index_value = [$index_value];
2216             }
2217            
2218 0 0       0 println "13.8.3.6" if $debug;
2219 0         0 $index_value = $self->_5_1_2_expansion($map_context, $key, $index_value, frameExpansion => $frameExpansion, ordered => $ordered);
2220 0 0       0 println(Data::Dumper->Dump([$index_value], ['*index_value'])) if $debug;
2221            
2222 0 0       0 println "13.8.3.7" if $debug;
2223 0         0 foreach my $item (@$index_value) {
2224 0         0 my $__indent = indent();
2225 0 0       0 println '-----------------------------------------------------------------' if $debug;
2226 0 0       0 println "13.8.3.7 [$item]" if $debug;
2227 0 0 0     0 if ($self->_cm_contains($container_mapping, '@graph') and not $self->_is_graph_object($item)) {
2228 0 0       0 println(Data::Dumper->Dump([$container_mapping], ['*container_mapping'])) if $debug;
2229 0 0       0 println "13.8.3.7.1" if $debug;
2230 0 0       0 $item = {'@graph' => (ref($item) eq 'ARRAY') ? $item : [$item]};
2231 0 0       0 println(Data::Dumper->Dump([$item], ['*item'])) if $debug;
2232             }
2233              
2234 0 0 0     0 if ($self->_cm_contains($container_mapping, '@index') and $index_key ne '@index' and $expanded_index ne '@none') {
    0 0        
    0 0        
    0 0        
      0        
      0        
      0        
2235 0 0       0 println "13.8.3.7.2 " . Data::Dumper->Dump([$index_key], ['index_key']) if $debug;
2236 0 0       0 println "13.8.3.7.2.1" if $debug;
2237 0         0 my $re_expanded_index = $self->_5_3_2_value_expand($activeCtx, $index_key, $index);
2238 0 0       0 println "13.8.3.7.2.2" if $debug;
2239 0         0 my $expanded_index_key = $self->_5_2_2_iri_expansion($activeCtx, $index_key, vocab => 1);
2240 0 0       0 println "13.8.3.7.2.3" if $debug;
2241 0         0 my $index_property_values = [$re_expanded_index];
2242 0 0       0 if (exists $item->{$expanded_index_key}) {
2243 0         0 my $v = $item->{$expanded_index_key};
2244 0 0       0 if (ref($v) eq 'ARRAY') {
2245 0         0 push(@{$index_property_values}, @$v);
  0         0  
2246             } else {
2247 0         0 push(@{$index_property_values}, $v);
  0         0  
2248             }
2249             }
2250 0 0       0 println "13.8.3.7.2.4" if $debug;
2251 0         0 $item->{$expanded_index_key} = $index_property_values;
2252              
2253 0 0       0 if ($self->_is_value_object($item)) {
2254 0         0 my @keys = sort keys %$item;
2255 0 0       0 if (scalar(@keys) > 1) {
2256 0         0 die 'invalid value object';
2257             }
2258             }
2259             } elsif ($self->_cm_contains($container_mapping, '@index') and not exists $item->{'@index'} and $expanded_index ne '@none') {
2260 0 0       0 println "13.8.3.7.3" if $debug;
2261 0         0 $item->{'@index'} = $index;
2262 0 0       0 println(Data::Dumper->Dump([$item], ['*item'])) if $debug;
2263             } elsif ($self->_cm_contains($container_mapping, '@id') and not exists $item->{'@id'} and $expanded_index ne '@none') {
2264 0 0       0 println "13.8.3.7.4" if $debug;
2265 0         0 $expanded_index = $self->_5_2_2_iri_expansion($activeCtx, $index, documentRelative => 1);
2266 0         0 $item->{'@id'} = $expanded_index;
2267             } elsif ($self->_cm_contains($container_mapping, '@type') and $expanded_index ne '@none') {
2268 0 0       0 println "13.8.3.7.5" if $debug;
2269 0         0 my $types = [$expanded_index];
2270 0 0       0 if (exists $item->{'@type'}) {
2271 0         0 my $v = $item->{'@type'};
2272 0 0       0 if (ref($v) eq 'ARRAY') {
2273 0         0 push(@$types, @{$item->{'@type'}});
  0         0  
2274             } else {
2275 0         0 push(@$types, $v);
2276             }
2277             }
2278 0         0 $item->{'@type'} = $types;
2279 0 0       0 println "13.8.3.7.5 " . Data::Dumper->Dump([$types], ['types']) if $debug;
2280             }
2281            
2282 0 0       0 println "13.8.3.7.6" if $debug;
2283 0         0 push(@$expandedValue, $item);
2284             }
2285             }
2286 0 0       0 println "13.8 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2287             } else {
2288 1 50       13 println "13.9" if $debug;
2289 1         32 $expandedValue = $self->_5_1_2_expansion($activeCtx, $key, $value, frameExpansion => $frameExpansion, ordered => $ordered); # 13.9
2290 1 50       3 println "13.9 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2291             }
2292            
2293             # warn Dumper($expandedValue);
2294 1 50       4 if (not(defined($expandedValue))) {
2295 0 0       0 println "13.10 going to next element key" if $debug;
2296 0         0 next; # 13.10
2297             }
2298            
2299 1 50 33     4 if ($self->_cm_contains($container_mapping, '@list') and not $self->_is_list_object($expandedValue)) {
2300             # 13.11
2301 0 0       0 println "13.11" if $debug;
2302 0 0       0 unless (ref($expandedValue) eq 'ARRAY') {
2303 0         0 $expandedValue = [$expandedValue];
2304             }
2305 0         0 $expandedValue = { '@list' => $expandedValue };
2306 0 0       0 println "13.11 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2307             }
2308              
2309 1 0 33     8 if ($self->_cm_contains($container_mapping, '@graph') and not($self->_cm_contains($container_mapping, '@id')) and not($self->_cm_contains($container_mapping, '@index'))) {
      33        
2310             # https://github.com/w3c/json-ld-api/issues/311
2311             # 13.12
2312 0 0       0 println "13.12" if $debug;
2313 0 0       0 if (ref($expandedValue) ne 'ARRAY') {
2314 0         0 $expandedValue = [$expandedValue];
2315             }
2316 0         0 my @values;
2317 0         0 foreach my $ev (@$expandedValue) {
2318 0 0       0 println "13.12.1" if $debug;
2319 0 0       0 my $av = (ref($ev) eq 'ARRAY') ? $ev : [$ev];
2320 0         0 push(@values, {'@graph' => $av});
2321             }
2322 0         0 $expandedValue = \@values;
2323 0 0       0 println "13.12 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2324             }
2325            
2326 1 50       11 if ($tdef->{'reverse'}) {
2327             # 13.13
2328 0 0       0 println "13.13" if $debug;
2329 0 0       0 unless (exists $result->{'@reverse'}) {
2330 0 0       0 println "13.13.1" if $debug;
2331 0         0 $result->{'@reverse'} = {};
2332             }
2333            
2334 0 0       0 println "13.13.2" if $debug;
2335 0         0 my $reverse_map = $result->{'@reverse'};
2336            
2337 0 0       0 if (ref($expandedValue) ne 'ARRAY') {
2338 0 0       0 println "13.13.3" if $debug;
2339 0         0 $expandedValue = [$expandedValue];
2340             }
2341            
2342 0         0 foreach my $item (@$expandedValue) {
2343 0 0       0 println "13.13.4" if $debug;
2344 0 0 0     0 if ($self->_is_value_object($item) or $self->_is_list_object($item)) {
2345 0 0       0 println "13.13.4.1" if $debug;
2346 0         0 die 'invalid reverse property value';
2347             }
2348            
2349 0 0       0 unless (exists $reverse_map->{$expandedProperty}) {
2350 0 0       0 println "13.13.4.2" if $debug;
2351 0         0 $reverse_map->{$expandedProperty} = [];
2352             }
2353            
2354 0 0       0 println "13.13.4.3" if $debug;
2355 0         0 push(@{ $reverse_map->{$expandedProperty} }, $item);
  0         0  
2356             }
2357 0 0       0 println "13.13 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2358             } else {
2359             # 13.14
2360 1 50       6 println "13.14" if $debug;
2361 1 50       5 unless (exists $result->{$expandedProperty}) {
2362 1 50       4 println "13.14.1" if $debug;
2363 1         7 $result->{$expandedProperty} = []; # 13.14.1
2364             }
2365              
2366 1 50       3 println "13.14.2 appending to result[$expandedProperty]: " . Data::Dumper->Dump([$expandedValue], ['expandedValue']) if $debug;
2367 1 50       13 if (ref($expandedValue) eq 'ARRAY') {
    50          
2368 0         0 push(@{$result->{$expandedProperty}}, @$expandedValue); # 13.14.2
  0         0  
2369             } elsif (ref($expandedValue)) {
2370 1 50       5 println "setting result[$expandedProperty]" if $debug;
2371 1         2 push(@{$result->{$expandedProperty}}, $expandedValue); # 13.14.2
  1         4  
2372             }
2373 1 50       5 println "13.14 resulting in " . Data::Dumper->Dump([$expandedValue], ['*expandedValue']) if $debug;
2374             }
2375             }
2376 1         15 $self->_5_1_2_expansion_step_14($activeCtx, $type_scoped_ctx, $result, $activeProp, $input_type, $nests, $ordered, $frameExpansion, $element);
2377             }
2378              
2379             sub _5_1_2_expansion_step_14 {
2380 1     1   2 my $self = shift;
2381 1         2 my $activeCtx = shift;
2382 1         2 my $type_scoped_ctx = shift;
2383 1         3 my $result = shift;
2384 1         2 my $activeProp = shift;
2385 1         2 my $input_type = shift;
2386 1         2 my $nests = shift;
2387 1         1 my $ordered = shift;
2388 1         2 my $frameExpansion = shift;
2389 1         2 my $element = shift;
2390            
2391             # https://github.com/w3c/json-ld-api/issues/262
2392 1 50       8 println "14" if $debug;
2393 1         7 my @keys = sort keys %$nests; # https://github.com/w3c/json-ld-api/issues/295
2394 1         3 foreach my $nesting_key (@keys) {
2395 0         0 delete $nests->{$nesting_key};
2396             # 14
2397 0         0 my $__indent = indent();
2398 0 0       0 println "14 [$nesting_key]" if $debug;
2399 0 0       0 println "14.1" if $debug;
2400             # next unless (exists $element->{$nesting_key});
2401 0         0 my $nested_values = $element->{$nesting_key}; # 14.1
2402 0 0       0 if (not defined $nested_values) {
2403 0         0 $nested_values = [];
2404             }
2405 0 0 0     0 if (not(ref($nested_values)) or ref($nested_values) ne 'ARRAY') {
2406 0         0 $nested_values = [$nested_values];
2407             }
2408              
2409 0 0       0 println "14.2" if $debug;
2410 0 0       0 println(Data::Dumper->Dump([$nesting_key, $element, $nested_values], [qw(nesting_key element nested_values)])) if $debug;
2411 0         0 foreach my $nested_value (@$nested_values) {
2412 0         0 my $__indent = indent();
2413 0 0       0 println '-----------------------------------------------------------------' if $debug;
2414 0 0       0 println "14.2 loop iteration" if $debug;
2415 0 0       0 if (ref($nested_value) ne 'HASH') {
2416 0 0       0 println "14.2.1 " . Data::Dumper->Dump([$nested_value], ['*invalid_nest_value']) if $debug;
2417 0         0 die 'invalid @nest value'; # 14.2.1
2418             }
2419            
2420 0         0 my @keys = keys %$nested_value;
2421 0         0 my %expandedKeys = map { $_ => 1 } map { $self->_5_2_2_iri_expansion($activeCtx, $_) } @keys;
  0         0  
  0         0  
2422 0 0       0 if (exists $expandedKeys{'@value'}) {
2423 0 0       0 println "14.2.1 " . Data::Dumper->Dump([$nested_value], ['*invalid_nest_value']) if $debug;
2424 0         0 die 'invalid @nest value';
2425             }
2426            
2427 0 0       0 println "14.2.2 ENTER =================> call to _5_1_2_expansion_step_13" if $debug;
2428 0         0 my $__indent_2 = indent();
2429 0         0 $self->_5_1_2_expansion_step_13($activeCtx, $type_scoped_ctx, $result, $activeProp, $input_type, $nests, $ordered, $frameExpansion, $nested_value); # 14.2.2
2430              
2431             }
2432             }
2433 1 50       3 println "after 14 resulting in " . Data::Dumper->Dump([$result], ['*result']) if $debug;
2434             }
2435              
2436             sub _5_2_2_iri_expansion {
2437 6     6   12 my $self = shift;
2438 6         8 my $activeCtx = shift;
2439 6         7 my $value = shift;
2440 6 50       11 println "ENTER =================> _5_2_2_iri_expansion($value)" if $debug;
2441 6         11 my $__indent = indent();
2442 6         16 my %args = @_;
2443 6         10 my %acceptable = map { $_ => 1 } qw(documentRelative vocab localCtx defined);
  24         46  
2444 6         16 foreach my $k (keys %args) {
2445 5 50       24 die "Not a recognized IRI expansion algorithm argument: $k" unless exists $acceptable{$k};
2446             }
2447 6   100     19 my $vocab = $args{vocab} // 0;
2448 6   100     17 my $documentRelative = $args{documentRelative} // 0;
2449 6   50     23 my $localCtx = $args{localCtx} // {};
2450 6   50     17 my $defined = $args{'defined'} // {};
2451 6         10 local($Data::Dumper::Indent) = 0;
2452 6 50       13 println(Data::Dumper->Dump([$activeCtx], ['*activeCtx'])) if $debug;
2453 6 50       8 println(Data::Dumper->Dump([$localCtx], ['*localCtx'])) if $debug;
2454 6 50       12 println(Data::Dumper->Dump([$defined], ['*defined'])) if $debug;
2455            
2456             # 5.2.2 algorithm
2457            
2458 6 100 66     20 unless (defined($value) and not exists $keywords{$value}) {
2459 2 50       4 println "1 returning from _5_2_2_iri_expansion: undefined/keyword value" if $debug;
2460 2         8 return $value;
2461             }
2462            
2463 4 50       23 if ($value =~ /^@[A-Za-z]+$/) {
2464 0 0       0 println "2" if $debug;
2465 0         0 warn "IRI expansion attempted on a term that looks like a keyword: $value\n"; # 2
2466 0         0 return;
2467             }
2468            
2469 4 50 33     15 if (defined($localCtx) and my $v = $localCtx->{$value}) {
2470 0 0       0 unless ($defined->{$v}) {
2471 0 0       0 println "3" if $debug;
2472 0         0 $self->_4_2_2_create_term_definition($activeCtx, $localCtx, $value, $defined); # 3
2473             }
2474             }
2475              
2476 4 50       9 if (my $tdef = $self->_ctx_term_defn($activeCtx, $value)) {
2477 0   0     0 my $i = $tdef->{'iri_mapping'} // '';
2478 0 0       0 if ($keywords{$i}) {
2479 0 0       0 println "4 returning from _5_2_2_iri_expansion with a keyword: $i" if $debug;
2480 0         0 return $i; # 4
2481             }
2482             }
2483            
2484 4 50 66     18 if ($vocab and my $tdef = $self->_ctx_term_defn($activeCtx, $value)) {
2485 0         0 my $i = $tdef->{'iri_mapping'};
2486 0 0       0 println "5 returning from _5_2_2_iri_expansion with iri mapping from active context: $i" if $debug;
2487 0         0 return $i; # 5
2488             }
2489            
2490 4 100       25 if ($value =~ /.:/) {
2491             # 6
2492 1 50       7 println "6" if $debug;
2493 1 50       3 println "6.1" if $debug;
2494 1         13 my ($prefix, $suffix) = split(/:/, $value, 2); # 6.1
2495            
2496 1 50 33     8 if ($prefix eq '_' or $suffix =~ m{^//}) {
2497 1 50       5 println "6.2 returning from _5_2_2_iri_expansion: already an absolute IRI or blank node identifier: $value" if $debug;
2498 1         5 return $value; # 6.2
2499             }
2500            
2501 0 0 0     0 if ($localCtx and exists $localCtx->{$prefix} and not($defined->{$prefix})) {
      0        
2502 0 0       0 println "6.3" if $debug;
2503 0         0 $self->_4_2_2_create_term_definition($activeCtx, $localCtx, $prefix, $defined);
2504             }
2505            
2506 0         0 my $tdef = $self->_ctx_term_defn($activeCtx, $prefix);
2507 0 0 0     0 if ($tdef and $tdef->{'iri_mapping'} and $tdef->{'prefix_flag'}) {
      0        
2508 0         0 my $i = $tdef->{'iri_mapping'} . $suffix;
2509 0 0       0 println "6.4 returning from _5_2_2_iri_expansion with concatenated iri mapping and suffix: $i" if $debug;
2510 0         0 return $i;
2511             }
2512            
2513 0 0       0 if ($self->_is_abs_iri($value)) {
2514 0 0       0 println "6.5 returning from _5_2_2_iri_expansion with absolute IRI: $value" if $debug;
2515 0         0 return $value;
2516             }
2517             }
2518            
2519 3 100 66     20 if ($vocab and exists $activeCtx->{'@vocab'}) {
    50          
2520 2         7 my $i = $activeCtx->{'@vocab'} . $value;
2521 2 50       6 println "7 returning from _5_2_2_iri_expansion with concatenated vocabulary mapping and value: $i" if $debug;
2522 2         9 return $i;
2523             } elsif ($documentRelative) {
2524             # 8
2525 0 0       0 println "8" if $debug;
2526 0         0 my $base = $activeCtx->{'@base'};
2527 0 0       0 if (defined $base) {
2528 0         0 my $i = IRI->new(value => $value, base => $base);
2529 0         0 $value = $i->abs;
2530             }
2531             }
2532              
2533 1 50       3 println "9 returning from _5_2_2_iri_expansion with final value: $value" if $debug;
2534 1         3 return $value; # 9
2535             }
2536            
2537             sub _5_3_2_value_expand {
2538 1     1   3 my $self = shift;
2539 1         2 my $activeCtx = shift;
2540 1         9 my $activeProp = shift;
2541 1         3 my $value = shift;
2542 1         13 my $_vs = Data::Dumper->new([$value], ['value'])->Terse(1)->Dump([$value], ['value']);
2543 1 50       134 println "ENTER =================> _5_3_2_value_expand($_vs)" if $debug;
2544 1         4 my $__indent = indent();
2545            
2546 1         3 my $tdef = $self->_ctx_term_defn($activeCtx, $activeProp);
2547              
2548 1 50       8 if (exists $tdef->{'type_mapping'}) {
2549 0 0 0     0 if ($tdef->{'type_mapping'} eq '@id' and _is_string($value)) {
2550 0         0 my $iri = $self->_5_2_2_iri_expansion($activeCtx, $value, documentRelative => 1);
2551 0 0       0 println "1 returning from _5_3_2_value_expand with new map containing \@id: $iri" if $debug;
2552 0         0 return { '@id' => $iri }; # 1
2553             }
2554              
2555 0 0 0     0 if ($tdef->{'type_mapping'} eq '@vocab' and _is_string($value)) {
2556 0         0 my $iri = $self->_5_2_2_iri_expansion($activeCtx, $value, vocab => 1, documentRelative => 1);
2557 0 0       0 println "1 returning from _5_3_2_value_expand with new map containing vocab \@id: $iri" if $debug;
2558 0         0 return { '@id' => $iri }; # 2
2559             }
2560             }
2561            
2562 1 50       3 println "3" if $debug;
2563 1         3 my $result = { '@value' => $value }; # 3
2564            
2565 1         3 my $tm = $tdef->{'type_mapping'};
2566 1 50 33     26 if (exists($tdef->{'type_mapping'}) and $tm ne '@id' and $tm ne '@vocab' and $tm ne '@none') {
    50 33        
      0        
2567 0 0       0 println "4" if $debug;
2568 0         0 $result->{'@type'} = $tm; # 4
2569             } elsif (_is_string($value)) {
2570 1 50       6 println "5" if $debug;
2571 1 50       3 println "5.1" if $debug;
2572 1 50       6 my $language = (exists $tdef->{'language_mapping'}) ? $tdef->{'language_mapping'} : $activeCtx->{'@language'}; # 5.1
2573              
2574 1 50       3 println "5.2" if $debug;
2575 1 50       4 my $direction = (exists $tdef->{'direction_mapping'}) ? $tdef->{'direction_mapping'} : $activeCtx->{'@direction'}; # 5.2
2576              
2577 1 50       3 if (defined($language)) {
2578 0 0       0 println "5.3" if $debug;
2579 0         0 $result->{'@language'} = $language; # 5.3
2580             }
2581            
2582 1 50       3 if (defined($direction)) {
2583 0 0       0 println "5.4" if $debug;
2584 0         0 $result->{'@direction'} = $direction; # 5.4
2585             }
2586             }
2587            
2588 1 50       22 println "6 returning from _5_3_2_value_expand with final result" if $debug;
2589 1         5 return $result; # 6
2590             }
2591              
2592             sub _6_1_2_compaction {
2593 0     0     my $self = shift;
2594 0           my $activeCtx = shift;
2595 0           my $inverseCtx = shift;
2596 0           my $activeProp = shift;
2597 0           my $element = shift;
2598             {
2599 2     2   28 no warnings 'uninitialized';
  2         4  
  2         9898  
  0            
2600 0 0         println "ENTER =================> _6_1_2_compaction('$activeProp')" if $debug;
2601             }
2602 0           my $__indent = indent();
2603             # local($Data::Dumper::Indent) = 0;
2604             # println(Data::Dumper->Dump([$activeCtx], ['activeCtx'])) if $debug;
2605             # println(Data::Dumper->Dump([$activeProp], ['activeProp'])) if $debug;
2606 0 0         println(Data::Dumper->Dump([$element], ['element'])) if $debug;
2607 0           my %args = @_;
2608 0   0       my $compactArrays = $args{compactArrays} // 0;
2609 0   0       my $ordered = $args{ordered} // 0;
2610            
2611 0 0         println "1" if $debug;
2612 0           my $type_scoped_ctx = clone($activeCtx);
2613            
2614 0 0         if (_is_scalar($element)) {
2615 0 0         println "2 returning scalar " . Data::Dumper->Dump([$element], [qw(element)]) if $debug;
2616 0           return $element;
2617             }
2618            
2619 0 0         if (ref($element) eq 'ARRAY') {
2620 0 0         println "3" if $debug;
2621 0 0         println "3.1" if $debug;
2622 0           my $result = [];
2623              
2624 0 0         println "3.2" if $debug;
2625 0           foreach my $item (@$element) {
2626 0 0         println "3.2.1" if $debug;
2627 0           my $compactedItem = $self->_6_1_2_compaction($activeCtx, $inverseCtx, $activeProp, $item, %args);
2628 0 0         if (defined($compactedItem)) {
2629 0 0         println "3.2.2" if $debug;
2630 0           push(@$result, $compactedItem);
2631             }
2632             }
2633            
2634 0   0       my $not_graph = ($activeProp // '') ne '@graph';
2635 0   0       my $not_set = ($activeProp // '') ne '@set';
2636 0           my $tdef = $self->_ctx_term_defn($activeCtx, $activeProp);
2637 0           my $container_mapping = $tdef->{'container_mapping'};
2638             # https://github.com/w3c/json-ld-api/issues/334
2639             # if (scalar(@$result) == 1 and $compactArrays and (($not_graph and $not_set) or (not($self->_cm_contains_any($container_mapping, '@list', '@set')) ))) {
2640 0 0 0       if (scalar(@$result) == 1 and (($not_graph and $not_set) or (not($self->_cm_contains_any($container_mapping, '@list', '@set')) and $compactArrays))) {
      0        
2641             # https://github.com/w3c/json-ld-api/issues/334
2642 0 0         println "3.3" if $debug;
2643 0           $result = $result->[0];
2644             }
2645            
2646 0 0         println "3.4 returning " . Data::Dumper->Dump([$result], [qw(result)]) if $debug;
2647 0           return $result;
2648             }
2649            
2650 0 0         println "4 element is a map" if $debug;
2651            
2652 0 0         if (exists $activeCtx->{'previous_context'}) {
2653 0 0         if (not exists $element->{'@value'}) {
2654 0           my @keys = keys %$element;
2655 0 0 0       unless (scalar(@keys) == 1 and $keys[0] eq '@id') {
2656 0 0         println "5" if $debug;
2657 0           $activeCtx = $activeCtx->{'previous_context'};
2658             }
2659             }
2660             }
2661            
2662 0           my $tdef = $self->_ctx_term_defn($activeCtx, $activeProp);
2663 0 0         if (exists $tdef->{'@context'}) {
2664 0 0         println "6" if $debug;
2665 0 0         println "6.1" if $debug;
2666 0           $activeCtx = $self->_4_1_2_ctx_processing($activeCtx, $tdef->{'@context'}, override_protected => 1);
2667            
2668 0 0         println "6.2" if $debug;
2669 0           $inverseCtx = $self->_4_3_inverse_context_creation($activeCtx);
2670             }
2671            
2672 0 0 0       if (exists $element->{'@value'} or exists $element->{'@id'}) {
2673 0 0         println "possibly 7" if $debug;
2674 0           my $v = $self->_6_3_value_compaction($activeCtx, $inverseCtx, $activeProp, $element);
2675 0   0       my $tm = $tdef->{'type_mapping'} // '';
2676 0 0 0       if (_is_scalar($v) or $tm eq '@json') {
2677 0 0         println "7 returning " . Data::Dumper->Dump([$v], [qw(result)]) if $debug;
2678 0           return $v;
2679             }
2680             }
2681            
2682 0           my $container_mapping = $tdef->{'container_mapping'};
2683 0 0 0       if (_is_list_object($element) and $self->_cm_contains($container_mapping, '@list')) {
2684 0 0         println "8" if $debug;
2685 0           my $result = $self->_6_1_2_compaction($activeCtx, $inverseCtx, $activeProp, $element->{'@list'}, %args);
2686 0 0         println "8 returning " . Data::Dumper->Dump([$result], [qw(result)]) if $debug;
2687 0           return $result;
2688             }
2689              
2690 0 0         println "9" if $debug;
2691 0   0       my $insideReverse = (defined($activeProp) and $activeProp eq '@reverse');
2692            
2693 0 0         println "10" if $debug;
2694 0           my $result = {};
2695            
2696 0 0         if (exists $element->{'@type'}) {
2697 0 0         println "11" if $debug;
2698 0           my @types = _values_from_scalar_or_array($element->{'@type'});
2699             # my @types = @{ $element->{'@type'} };
2700 0           my @compacted_types = map { $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $_, vocab => 1) } @types;
  0            
2701 0           foreach my $term (sort @compacted_types) {
2702 0 0         println "11.1" if $debug;
2703 0           my $tdef = $self->_ctx_term_defn($type_scoped_ctx, $term);
2704 0 0         if (exists $tdef->{'@context'}) {
2705 0 0         println "11.1.1" if $debug;
2706 0           $activeCtx = $self->_4_1_2_ctx_processing($activeCtx, $tdef->{'@context'});
2707              
2708 0 0         println "11.1.2" if $debug;
2709 0           $inverseCtx = $self->_4_3_inverse_context_creation($activeCtx);
2710             }
2711             }
2712             }
2713            
2714 0 0         println "12" if $debug;
2715 0           foreach my $expandedProperty (sort keys %$element) {
2716 0           my $__indent = indent();
2717 0 0         println "----------------------------------------------------------------" if $debug;
2718 0           my $expandedValue = $element->{$expandedProperty};
2719 0 0         println "12 [$expandedProperty]" if $debug;
2720              
2721 0   0       my $tdef = $self->_ctx_term_defn($activeCtx, $activeProp) || {};
2722 0           my $container_mapping = $tdef->{'container_mapping'};
2723            
2724 0 0 0       if ($expandedProperty eq '@id') {
    0          
    0          
    0          
    0          
    0          
2725 0 0         println "12.1" if $debug;
2726 0 0         if (_is_string($expandedValue)) {
2727 0 0         println "12.1.1" if $debug;
2728 0           my $compactedValue = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedValue, vocab => 0);
2729              
2730 0 0         println "12.1.2" if $debug;
2731 0           my $alias = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedProperty, vocab => 1);
2732            
2733 0 0         println "12.1.3" if $debug;
2734 0           $result->{$alias} = $compactedValue;
2735 0           next;
2736             }
2737             } elsif ($expandedProperty eq '@type') {
2738 0 0         println "12.2" if $debug;
2739 0           my $compactedValue;
2740 0 0         if (_is_string($expandedValue)) {
2741 0 0         println "12.2.1" if $debug;
2742 0           $compactedValue = $self->_6_2_2_iri_compaction($type_scoped_ctx, $inverseCtx, $expandedValue, vocab => 1);
2743             } else {
2744 0 0         println "12.2.2" if $debug;
2745 0 0         println "12.2.2.1" if $debug;
2746 0           $compactedValue = [];
2747              
2748 0 0         println "12.2.2.2" if $debug;
2749 0           foreach my $expandedType (@{ $expandedValue }) {
  0            
2750 0 0         println "12.2.2.2.1" if $debug;
2751 0           my $term = $self->_6_2_2_iri_compaction($type_scoped_ctx, $inverseCtx, $expandedType, vocab => 1);
2752            
2753 0 0         println "12.2.2.2.2" if $debug;
2754 0           push(@{ $compactedValue }, $term);
  0            
2755             }
2756              
2757 0 0         println "12.2.2.3" if $debug;
2758 0 0         if (scalar(@$compactedValue) == 1) {
2759 0           $compactedValue = $compactedValue->[0];
2760             }
2761             }
2762              
2763 0 0         println "12.2.3" if $debug;
2764 0           my $alias = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedProperty, vocab => 1);
2765              
2766 0 0         println "12.2.4" if $debug;
2767 0           my $tdef = $self->_ctx_term_defn($activeCtx, $alias);
2768 0           my $container_mapping = $tdef->{'container_mapping'};
2769 0 0         my $as_array = ($self->_cm_contains($container_mapping, '@set'))
2770             ? 1
2771             : not($compactArrays);
2772 0 0         println "12.2.5" if $debug;
2773 0           $self->_add_value($result, $alias, $compactedValue, as_array => $as_array);
2774              
2775 0 0         println "12.2.6" if $debug;
2776 0           next;
2777             } elsif ($expandedProperty eq '@reverse') {
2778 0 0         println "12.3" if $debug;
2779 0 0         println "12.3.1" if $debug;
2780 0           my $compactedValue = $self->_6_1_2_compaction($activeCtx, $inverseCtx, '@reverse', $expandedValue, compactArrays => $compactArrays, ordered => $ordered);
2781            
2782 0           foreach my $property (keys %$compactedValue) {
2783 0 0         println "12.3.2 [$property]" if $debug;
2784 0           my $value = $compactedValue->{$property};
2785            
2786 0           my $tdef = $self->_ctx_term_defn($activeCtx, $property);
2787 0 0         if ($tdef->{'reverse'}) {
2788 0 0         println "12.3.2.1" if $debug;
2789 0 0         println "12.3.2.1.1" if $debug;
2790 0           my $container_mapping = $tdef->{'container_mapping'};
2791 0           my $as_array;
2792 0 0         if ($self->_cm_contains($container_mapping, '@set')) {
2793 0           $as_array = 1;
2794             } else {
2795 0           $as_array = not($compactArrays);
2796             }
2797            
2798 0 0         println "12.3.2.1.2" if $debug;
2799 0           $self->_add_value($result, $property, $value, as_array => $as_array);
2800            
2801 0 0         println "12.3.2.1.3" if $debug;
2802 0           delete $compactedValue->{$property};
2803             }
2804             }
2805            
2806 0           my @keys = keys %$compactedValue;
2807 0 0         if (scalar(@keys)) {
2808 0 0         println "12.3.3" if $debug;
2809 0 0         println "12.3.3.1" if $debug;
2810 0           my $alias = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@reverse', vocab => 1);
2811            
2812 0 0         println "12.3.3.2" if $debug;
2813 0           $result->{$alias} = $compactedValue;
2814             }
2815            
2816 0 0         println "12.3.4" if $debug;
2817 0           next;
2818             } elsif ($expandedProperty eq '@preserve') {
2819 0 0         println "12.4" if $debug;
2820 0 0         println "12.4.1" if $debug;
2821 0           my $compactedValue = $self->_6_1_2_compaction($activeCtx, $inverseCtx, $activeProp, $expandedValue, compactArrays => $compactArrays, ordered => $ordered);
2822            
2823 0 0 0       unless (ref($expandedValue) eq 'ARRAY' and scalar(@$expandedValue) == 0) {
2824 0 0         println "12.4.2" if $debug;
2825 0           $result->{'@preserve'} = $compactedValue;
2826             }
2827             } elsif ($expandedProperty eq '@index' and $self->_cm_contains($container_mapping, '@index')) {
2828 0 0         println "12.5" if $debug;
2829 0           next;
2830             } elsif ($expandedProperty =~ /^@(direction|index|language|value)$/) {
2831 0 0         println "12.6" if $debug;
2832 0 0         println "12.6.1" if $debug;
2833 0           my $alias = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedProperty, vocab => 1);
2834            
2835 0 0         println "12.6.2" if $debug;
2836 0           $result->{$alias} = $expandedValue;
2837 0           next;
2838             }
2839            
2840 0 0 0       if (ref($expandedValue) eq 'ARRAY' and scalar(@$expandedValue) == 0) {
2841 0 0         println "12.7" if $debug;
2842 0 0         println "12.7.1" if $debug;
2843             # https://github.com/w3c/json-ld-api/issues/357
2844 0           my $item_active_property = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedProperty, value => $expandedValue, vocab => 1, 'reverse' => $insideReverse);
2845              
2846 0 0         println "12.7.2" if $debug;
2847 0           my $tdef = $self->_ctx_term_defn($activeCtx, $item_active_property);
2848 0           my $nest_result;
2849 0 0         if (exists $tdef->{'nest_value'}) {
2850 0           my $nest_term = $tdef->{'nest_value'};
2851 0 0         if ($nest_term ne '@nest') {
2852 0           die 'invalid @nest value';
2853             }
2854 0 0         if (not exists $result->{$nest_term}) {
2855 0           $result->{$nest_term} = {};
2856             }
2857 0 0         if (not exists $result->{$nest_term}{$item_active_property}) {
    0          
2858 0           $result->{$nest_term}{$item_active_property} = [];
2859             } elsif (ref($result->{$nest_term}{$item_active_property}) ne 'ARRAY') {
2860 0           $result->{$nest_term}{$item_active_property} = [$result->{$nest_term}{$item_active_property}];
2861             }
2862             } else {
2863 0 0         println "12.7.3" if $debug;
2864 0 0         if (not exists $result->{$item_active_property}) {
    0          
2865 0           $result->{$item_active_property} = [];
2866             } elsif (ref($result->{$item_active_property}) ne 'ARRAY') {
2867 0           $result->{$item_active_property} = [$result->{$item_active_property}];
2868             }
2869             }
2870             }
2871            
2872 0 0         println "12.8" if $debug;
2873            
2874 0           foreach my $expandedItem (@$expandedValue) {
2875 0 0         println "12.8.1" if $debug;
2876 0           my $item_active_property = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedProperty, value => $expandedItem, vocab => 1, 'reverse' => $insideReverse);
2877 0 0         println(Data::Dumper->Dump([$item_active_property], ['item_active_property'])) if $debug;
2878            
2879 0           my $tdef = $self->_ctx_term_defn($activeCtx, $item_active_property);
2880 0           my $nest_result;
2881 0 0         if (exists $tdef->{'nest_value'}) {
2882 0 0         println "12.8.2" if $debug;
2883 0           my $nest_term = $tdef->{'nest_value'};
2884 0 0         if ($nest_term ne '@nest') {
2885 0           die 'invalid @nest value';
2886             }
2887 0   0       $nest_result = $result->{$nest_term} // {};
2888             } else {
2889 0 0         println "12.8.3" if $debug;
2890 0           $nest_result = $result;
2891             }
2892            
2893 0 0         println "12.8.4 " . Data::Dumper->Dump([$container_mapping], [qw(container_mapping)]) if $debug;
2894 0           my $container = undef;
2895 0           my $container_mapping = $tdef->{'container_mapping'};
2896 0 0         if (defined $container_mapping) {
2897             # https://github.com/w3c/json-ld-api/issues/322
2898 0           $container = [grep { $_ ne '@set' } @$container_mapping]; # TODO unfiled issue on this
  0            
2899             # $container = (grep { $_ ne '@set' } @$container_mapping)[0];
2900             }
2901            
2902 0 0         println "12.8.5" if $debug;
2903 0   0       my $as_array = ($self->_cm_contains($container_mapping, '@set') or $item_active_property eq '@graph' or $item_active_property eq '@list');
2904            
2905 0 0         println "12.8.6" if $debug;
2906             my $_element = ((ref($expandedItem) ne 'HASH' or not exists $expandedItem->{'@list'}) and (not($self->_is_graph_object($expandedItem)) or not exists($expandedItem->{'@list'})))
2907             ? $expandedItem
2908 0 0 0       : $expandedItem->{'@list'};
2909 0           my $compactedItem = $self->_6_1_2_compaction($activeCtx, $inverseCtx, $item_active_property, $_element, %args);
2910            
2911 0 0         if ($self->_is_list_object($expandedItem)) {
2912 0 0         println "12.8.7" if $debug;
2913 0 0         if (ref($compactedItem) ne 'ARRAY') {
2914 0 0         println "12.8.7.1" if $debug;
2915 0           $compactedItem = [$compactedItem];
2916             }
2917            
2918 0 0         if (not $self->_cm_contains($container, '@list')) {
2919 0 0         println "12.8.7.2" if $debug;
2920 0 0         println "12.8.7.2.1" if $debug;
2921 0           my $key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@list', value => $compactedItem);
2922 0           $compactedItem = { $key => $compactedItem };
2923            
2924 0 0         println "12.8.7.2.2" if $debug;
2925 0 0         if (exists $expandedItem->{'@index'}) {
2926 0           my $key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@index');
2927 0           $compactedItem->{ $key } = $expandedItem->{'@index'};
2928             }
2929             }
2930             }
2931            
2932 0 0 0       if ($self->_is_graph_object($expandedItem)) {
    0          
2933 0           println "12.8.8"; # if $debug;
2934 0 0 0       if ($self->_cm_contains($container, '@graph') and $self->_cm_contains($container, '@id')) {
    0 0        
    0 0        
    0 0        
2935 0 0         println "12.8.8.1" if $debug;
2936 0 0         println "12.8.8.1.1" if $debug;
2937 0   0       my $map_object = ($nest_result->{$item_active_property} //= {});
2938            
2939 0 0         println "12.8.8.1.2" if $debug;
2940 0   0       my $map_key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedItem->{'@id'} // '@none', vocab => (not exists $expandedItem->{'@id'}));
2941            
2942 0 0         println "12.8.8.1.3" if $debug;
2943 0           $self->_add_value($map_object, $map_key, $compactedItem, as_array => $as_array);
2944             } elsif ($self->_cm_contains($container, '@graph') and $self->_cm_contains($container, '@index') and $self->_is_simple_graph_object($expandedItem)) {
2945 0 0         println "12.8.8.2" if $debug;
2946 0 0         println "12.8.8.2.1" if $debug;
2947 0   0       my $map_object = ($nest_result->{$item_active_property} //= {});
2948            
2949 0 0         println "12.8.8.2.2" if $debug;
2950 0   0       my $map_key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedItem->{'@index'} // '@none');
2951              
2952 0 0         println "12.8.8.2.3" if $debug;
2953 0           $self->_add_value($map_object, $map_key, $compactedItem, as_array => $as_array);
2954             } elsif ($container eq '@graph' and $self->_is_simple_graph_object($expandedItem)) {
2955 0 0         println "12.8.8.3" if $debug;
2956 0 0 0       if (ref($compactedItem) eq 'ARRAY' and scalar(@$compactedItem) > 1) {
2957 0 0         println "12.8.8.3.1" if $debug;
2958 0           my $k = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@included', value => $compactedItem);
2959 0           $compactedItem = {$k => $compactedItem};
2960             }
2961            
2962 0 0         println "12.8.8.3.2" if $debug;
2963 0           $self->_add_value($nest_result, $item_active_property, $compactedItem, as_array => $as_array);
2964             } elsif ($container ne '@graph') {
2965 0 0         println "12.8.8.4" if $debug;
2966 0 0         println "12.8.8.4.1" if $debug;
2967 0           my $key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@graph', vocab => 1);
2968 0           $compactedItem = { $key => $compactedItem };
2969            
2970 0 0         if (exists $expandedItem->{'@id'}) {
2971 0 0         println "12.8.8.4.2" if $debug;
2972 0           my $key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@id', vocab => 1);
2973 0           my $value = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $expandedItem->{'@id'});
2974 0           $compactedItem->{$key} = $value;
2975             }
2976            
2977            
2978 0 0         if (exists $expandedItem->{'@index'}) {
2979 0 0         println "12.8.8.4.3" if $debug;
2980 0           my $key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@index', vocab => 1);
2981 0           $compactedItem->{$key} = $expandedItem->{'@index'};
2982             }
2983            
2984 0 0         println "12.8.8.4.4" if $debug;
2985 0           $self->_add_value($nest_result, $item_active_property, $compactedItem, as_array => $as_array);
2986             }
2987             } elsif ($self->_cm_contains_any($container, '@language', '@index', '@id', '@type') and not $self->_cm_contains($container, '@graph')) {
2988             # TODO: spec text here is really confused since the type of `container` is scalar: "Otherwise, if container includes @language, @index, @id, or @type and container does not include @graph"
2989 0 0         println "12.8.9" if $debug;
2990 0 0         println "12.8.9.1" if $debug;
2991 0   0       my $map_object = ($nest_result->{$item_active_property} //= {});
2992            
2993 0 0         println "12.8.9.2" if $debug;
2994 0           my $compaction_value;
2995 0           foreach my $k ('@language', '@index', '@id', '@type') {
2996 0 0         $compaction_value = $k if ($self->_cm_contains_any($container, $k));
2997             }
2998 0           my $container_key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $compaction_value, vocab => 1);
2999            
3000 0 0         println "12.8.9.3" if $debug;
3001 0   0       my $tdef = $self->_ctx_term_defn($activeCtx, $item_active_property) // {};
3002 0   0       my $index_key = $tdef->{'index_mapping'} // '@index';
3003            
3004 0           my $map_key;
3005 0 0 0       if ($self->_cm_contains_any($container, '@language') and exists $expandedItem->{'@value'}) {
    0 0        
    0 0        
    0          
    0          
3006 0 0         println "12.8.9.4" if $debug;
3007 0           $compactedItem = $expandedItem->{'@value'};
3008 0           $map_key = $expandedItem->{'@language'};
3009             } elsif ($self->_cm_contains_any($container, '@index') and $index_key eq '@index') {
3010 0 0         println "12.8.9.5" if $debug;
3011 0           $map_key = $expandedItem->{'@index'};
3012             } elsif ($self->_cm_contains_any($container, '@index') and $index_key ne '@index') {
3013 0 0         println "12.8.9.6" if $debug;
3014             my @values = (ref($compactedItem) eq 'ARRAY')
3015 0 0         ? @{ $compactedItem->{$container_key} || [] }
3016 0 0         : ($compactedItem->{$container_key});
3017 0           $map_key = shift @values;
3018 0 0         if (scalar(@values)) {
3019 0           $compactedItem->{$container_key} = [@values];
3020             } else {
3021 0           delete $compactedItem->{$container_key};
3022             }
3023             } elsif ($self->_cm_contains_any($container, '@id')) {
3024 0 0         println "12.8.9.7" if $debug;
3025 0           $map_key = delete $compactedItem->{$container_key};
3026             } elsif ($self->_cm_contains_any($container, '@type')) {
3027 0 0         println "12.8.9.8" if $debug;
3028 0 0         println "12.8.9.8.1" if $debug;
3029 0 0         my @values = @{ $compactedItem->{$container_key} || [] };
  0            
3030 0           $map_key = shift @values;
3031            
3032 0 0         if (scalar(@values)) {
3033 0 0         println "12.8.9.8.2" if $debug;
3034 0           $compactedItem->{$container_key} = [@values];
3035             } else {
3036 0 0         println "12.8.9.8.3" if $debug;
3037 0           delete $compactedItem->{$container_key};
3038             }
3039            
3040 0           my @keys = map { $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $_, vocab => 1) } keys %$compactedItem;
  0            
3041 0 0 0       if (scalar(@keys) == 1 and $keys[0] eq '@id') {
3042 0 0         println "12.8.9.8.4" if $debug;
3043 0           $compactedItem = $self->_6_1_2_compaction($activeCtx, $inverseCtx, $item_active_property, {'@id' => $expandedItem});
3044             }
3045             }
3046            
3047 0 0         if (not defined($map_key)) {
3048 0 0         println "12.8.9.9" if $debug;
3049 0           $map_key = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, '@none', vocab => 1);
3050             }
3051            
3052 0 0         println "12.8.9.10 adding value to [$map_key]" if $debug;
3053 0           $self->_add_value($map_object, $map_key, $compactedItem, as_array => 1);
3054             } else {
3055 0 0         println "12.8.10" if $debug;
3056 0 0 0       if (not($compactArrays) and $as_array and ref($compactedItem) ne 'ARRAY') {
      0        
3057 0 0         println "12.8.10.1" if $debug;
3058 0           $compactedItem = [$compactedItem];
3059             }
3060            
3061 0 0         if (not exists $result->{$item_active_property}) {
3062 0 0         println "12.8.10.2" if $debug;
3063 0           $nest_result->{$item_active_property} = $compactedItem;
3064             } else {
3065 0 0         println "12.8.10.3" if $debug;
3066 0 0         if (ref($nest_result->{$item_active_property}) ne 'ARRAY') {
3067 0           $nest_result->{$item_active_property} = [$nest_result->{$item_active_property}];
3068             }
3069            
3070 0 0         if (ref($compactedItem) eq 'ARRAY') {
3071 0           push(@{$nest_result->{$item_active_property}}, @{ $compactedItem });
  0            
  0            
3072             } else {
3073 0           push(@{$nest_result->{$item_active_property}}, $compactedItem);
  0            
3074             }
3075             }
3076             }
3077             }
3078 0 0         println "after this 12 loop body " . Data::Dumper->Dump([$result], [qw(result)]) if $debug;
3079             }
3080              
3081 0 0         println "13 returning " . Data::Dumper->Dump([$result], [qw(result)]) if $debug;
3082 0           return $result;
3083            
3084             }
3085              
3086             sub _6_2_2_iri_compaction {
3087 0     0     my $self = shift;
3088 0           my $activeCtx = shift;
3089 0           my $inverseCtx = shift;
3090 0           my $var = shift;
3091 0           my %args = @_;
3092 0           my $value = $args{'value'};
3093 0   0       my $vocab = $args{'vocab'} || 0;
3094 0   0       my $reverse = $args{'reverse'} || 0;
3095             {
3096 2     2   16 no warnings 'uninitialized';
  2         5  
  2         6070  
  0            
3097 0 0         println "ENTER =================> _6_2_2_iri_compaction('$var')" if $debug;
3098             }
3099 0           my $__indent = indent();
3100             # println(Data::Dumper->Dump([$activeCtx], ['activeCtx'])) if $debug;
3101             # println(Data::Dumper->Dump([$inverseCtx], ['inverseCtx'])) if $debug;
3102            
3103 0 0         unless (defined($var)) {
3104 0 0         println "1 returning" if $debug;
3105 0           return;
3106             }
3107            
3108 0 0 0       if ($vocab and exists $inverseCtx->{$var}) {
3109 0 0         println "2" if $debug;
3110 0 0         println "2.1" if $debug;
3111 0           my $defaultLanguage;
3112 0 0         if (defined $activeCtx->{'default_base_direction'}) {
3113 0 0         println "2.1.1" if $debug;
3114 0           $defaultLanguage = join('_', $activeCtx->{'@language'}, $activeCtx->{'default_base_direction'});
3115             } else {
3116 0 0         println "2.1.2" if $debug;
3117 0 0         if (exists $activeCtx->{'@language'}) {
3118 0           $defaultLanguage = lc($activeCtx->{'@language'});
3119             } else {
3120 0           $defaultLanguage = '@none';
3121             }
3122             }
3123            
3124 0 0 0       if (ref($value) eq 'HASH' and exists $value->{'@preserve'}) {
3125 0 0         println "2.2" if $debug;
3126 0           $value = $value->{'@preserve'}[0];
3127             }
3128            
3129 0 0         println "2.3" if $debug;
3130 0           my $containers = [];
3131            
3132 0 0         println "2.4" if $debug;
3133 0           my $type_language = '@language';
3134 0           my $type_language_value = '@null';
3135            
3136 0 0 0       if (ref($value) eq 'HASH' and exists $value->{'@index'} and not $self->_is_graph_object($value)) {
      0        
3137 0 0         println "2.5" if $debug;
3138 0           push(@$containers, '@index', '@index@set');
3139             }
3140            
3141 0 0         if ($reverse) {
    0          
    0          
3142 0 0         println "2.6" if $debug;
3143 0           $type_language = '@type';
3144 0           $type_language_value = '@reverse';
3145 0           push(@$containers, '@set');
3146             } elsif ($self->_is_list_object($value)) {
3147 0 0         println "2.7" if $debug;
3148 0 0         if (not exists $value->{'@index'}) {
3149 0 0         println "2.7.1" if $debug;
3150 0           push(@$containers, '@list');
3151             }
3152            
3153 0 0         println "2.7.2" if $debug;
3154 0           my $list = $value->{'@list'};
3155            
3156 0 0         println "2.7.3" if $debug;
3157 0           my $common_type;
3158             my $common_language;
3159 0 0         unless (scalar(@$list)) {
3160 0           $common_language = $defaultLanguage;
3161             }
3162            
3163 0 0         println "2.7.4" if $debug;
3164 0           foreach my $item (@$list) {
3165 0 0         println "2.7.4.1" if $debug;
3166 0           my $item_language = '@none';
3167 0           my $item_type = '@none';
3168            
3169 0 0         if (exists $item->{'@value'}) {
3170 0 0         println "2.7.4.2" if $debug;
3171 0 0         if (exists $item->{'@direction'}) {
    0          
    0          
3172 0 0         println "2.7.4.2.1" if $debug;
3173 0   0       $item_language = lc(join('_', $item->{'@language'} // '', $item->{'@direction'}));
3174             } elsif (exists $item->{'@language'}) {
3175 0 0         println "2.7.4.2.2" if $debug;
3176 0           $item_language = lc($item->{'@language'});
3177             } elsif (exists $item->{'@type'}) {
3178 0 0         println "2.7.4.2.3" if $debug;
3179 0           $item_type = $item->{'@type'};
3180             } else {
3181 0 0         println "2.7.4.2.4" if $debug;
3182 0           $item_language = '@null';
3183             }
3184             } else {
3185 0 0         println "2.7.4.3" if $debug;
3186 0           $item_type = '@id';
3187             }
3188            
3189 0 0 0       if (not defined($common_language)) {
    0          
3190 0 0         println "2.7.4.4" if $debug;
3191 0           $common_language = $item_language;
3192             } elsif ($item_language ne $common_language and exists $item->{'@value'}) {
3193 0 0         println "2.7.4.5" if $debug;
3194 0           $common_language = '@none';
3195             }
3196            
3197 0 0         if (not defined($common_type)) {
    0          
3198 0 0         println "2.7.4.6" if $debug;
3199 0           $common_type = $item_type;
3200             } elsif ($item_type ne $common_type) {
3201 0 0         println "2.7.4.7" if $debug;
3202 0           $common_type = '@none';
3203             }
3204            
3205 0 0 0       if ($common_language eq '@none' and $common_type eq '@none') {
3206 0 0         println "2.7.4.8" if $debug;
3207 0           last;
3208             }
3209             }
3210            
3211 0 0         if (not defined($common_language)) {
3212 0 0         println "2.7.5" if $debug;
3213 0           $common_language = '@none';
3214             }
3215            
3216 0 0         if (not defined($common_type)) {
3217 0 0         println "2.7.6" if $debug;
3218 0           $common_type = '@none';
3219             }
3220            
3221 0 0         if ($common_type ne '@none') {
3222 0 0         println "2.7.7" if $debug;
3223 0           $type_language = '@type';
3224 0           $type_language_value = $common_type;
3225             } else {
3226 0 0         println "2.7.8" if $debug;
3227 0           $type_language_value = $common_language;
3228             }
3229            
3230             } elsif ($self->_is_graph_object($value)) {
3231 0 0         println "2.8" if $debug;
3232 0 0         if (exists $value->{'@index'}) {
3233 0 0         println "2.8.1" if $debug;
3234 0           push(@$containers, '@graph@index', '@graph@index@set');
3235             }
3236            
3237 0 0         if (exists $value->{'@id'}) {
3238 0 0         println "2.8.2" if $debug;
3239 0           push(@$containers, '@graph@id', '@graph@id@set');
3240             }
3241            
3242 0 0         println "2.8.3" if $debug;
3243 0           push(@$containers, '@graph', '@graph@set', '@set');
3244            
3245 0 0         if (not exists $value->{'@index'}) {
3246 0 0         println "2.8.4" if $debug;
3247 0           push(@$containers, '@graph@index', '@graph@index@set');
3248             }
3249            
3250 0 0         if (not exists $value->{'@id'}) {
3251 0 0         println "2.8.5" if $debug;
3252 0           push(@$containers, '@graph@id', '@graph@id@set');
3253             }
3254              
3255 0 0         println "2.8.6" if $debug;
3256 0           push(@$containers, '@index', '@index@set');
3257              
3258 0 0         println "2.8.7" if $debug;
3259 0           $type_language = '@type';
3260 0           $type_language_value = '@id';
3261             } else {
3262 0 0         println "2.9" if $debug;
3263 0 0         if ($self->_is_value_object($value)) {
3264 0 0         println "2.9.1" if $debug;
3265 0 0 0       if (exists $value->{'@direction'} and not exists $value->{'@index'}) {
    0 0        
    0          
3266 0 0         println "2.9.1.1" if $debug;
3267 0   0       $type_language_value = lc(join('_', $value->{'@language'} // '', $value->{'@direction'}));
3268 0           push(@$containers, '@language', '@language@set');
3269             } elsif (exists $value->{'@language'} and not exists $value->{'@index'}) {
3270 0 0         println "2.9.1.2" if $debug;
3271 0           $type_language_value = $value->{'@language'};
3272 0           push(@$containers, '@language', '@language@set');
3273             } elsif (exists $value->{'@type'}) {
3274 0 0         println "2.9.1.3" if $debug;
3275 0           $type_language_value = $value->{'@type'};
3276 0           $type_language = '@type';
3277             }
3278             } else {
3279 0 0         println "2.9.2" if $debug;
3280 0           $type_language = '@type';
3281 0           $type_language_value = '@id';
3282 0           push(@$containers, '@id', '@id@set', '@type', '@set@type');
3283             }
3284            
3285 0 0         println "2.9.3" if $debug;
3286 0           push(@$containers, '@set');
3287             }
3288            
3289 0 0         println "2.10" if $debug;
3290 0           push(@$containers, '@none');
3291            
3292             # unless (ref($value) eq 'HASH') {
3293             # Carp::cluck "unexpected non-HASH in IRI Compaction: " . Dumper($value);
3294             # }
3295             # if ($self->processing_mode ne 'json-ld-1.0' and not(exists $value->{'@index'})) {
3296 0 0 0       if ($self->processing_mode ne 'json-ld-1.0' and not(ref($value) eq 'HASH' and exists $value->{'@index'})) {
      0        
3297             # TODO: spec is missing the ref 'HASH' check
3298 0 0         println "2.11" if $debug;
3299 0           push(@$containers, '@index', '@index@set');
3300             }
3301            
3302 0 0         my @keys = (ref($value) eq 'HASH') ? keys %$value : ();
3303 0 0 0       if ($self->processing_mode ne 'json-ld-1.0' and scalar(@keys) == 1 and $keys[0] eq '@value') {
      0        
3304 0 0         println "2.12" if $debug;
3305 0           push(@$containers, '@language', '@language@set');
3306             }
3307            
3308 0 0         if (not defined($type_language_value)) {
3309 0 0         println "2.13" if $debug;
3310 0           $type_language_value = '@null';
3311             }
3312            
3313 0 0         println "2.14" if $debug;
3314 0           my $preferred_values = [];
3315            
3316 0 0         if ($type_language_value eq '@reverse') {
3317 0 0         println "2.15" if $debug;
3318 0           push(@$preferred_values, '@reverse');
3319             }
3320            
3321 0 0 0       if (($type_language_value eq '@id' or $type_language_value eq '@reverse') and ref($value) eq 'HASH' and exists $value->{'@id'}) {
      0        
      0        
3322             # TODO: spec is missing the ref 'HASH' check
3323 0 0         println "2.16" if $debug;
3324 0           my $compact_iri = $self->_6_2_2_iri_compaction($activeCtx, $activeCtx, $value->{'@id'}, vocab => 1);
3325 0           my $tdef = $self->_ctx_term_defn($activeCtx, $compact_iri);
3326 0 0 0       if ($tdef and $tdef->{'iri_mapping'} eq $value->{'@id'}) {
3327 0 0         println "2.16.1" if $debug;
3328 0           push(@$preferred_values, '@vocab', '@id', '@none');
3329             } else {
3330 0 0         println "2.16.2" if $debug;
3331 0           push(@$preferred_values, '@id', '@vocab', '@none');
3332             }
3333             } else {
3334 0 0         println "2.17" if $debug;
3335 0           push(@$preferred_values, $type_language_value, '@none');
3336 0 0 0       if ($self->_is_list_object($value) and not scalar(@{ $value->{'@list'} })) {
  0            
3337             # https://github.com/w3c/json-ld-api/issues/345
3338 0           $type_language = '@any';
3339             }
3340             }
3341            
3342 0 0         println "2.18" if $debug;
3343 0           push(@$preferred_values, '@any');
3344            
3345 0           my @underscored = map { substr($_, 1+index($_, '_')) } grep { /_/ } @$preferred_values;
  0            
  0            
3346 0 0         if (scalar(@underscored)) {
3347 0 0         println "2.19" if $debug;
3348 0           push(@$preferred_values, @underscored);
3349             }
3350            
3351 0 0         println "2.20" if $debug;
3352 0           my $term = $self->_4_4_2_term_selection($inverseCtx, $var, $containers, $type_language, $preferred_values);
3353              
3354 0 0         if (defined($term)) {
3355 0 0         println "2.21 returning: $term" if $debug;
3356 0           return $term;
3357             }
3358             }
3359            
3360 0 0 0       if ($vocab and exists $activeCtx->{'@vocab'}) {
3361 0 0         println "3" if $debug;
3362 0           my $vm = $activeCtx->{'@vocab'};
3363 0 0 0       if (substr($var, 0, length($vm)) eq $vm and length($var) > length($vm)) {
3364 0 0         println "3.1" if $debug;
3365 0           my $suffix = substr($var, length($vm));
3366 0           my $tdef = $self->_ctx_term_defn($activeCtx, $suffix);
3367 0 0         unless ($tdef) {
3368 0 0         println "3.1 returning: $suffix" if $debug;
3369 0           return $suffix;
3370             }
3371             }
3372             }
3373              
3374 0 0         println "4" if $debug;
3375 0           my $compact_iri = undef;
3376            
3377 0 0         println "5" if $debug;
3378 0           foreach my $term (keys %{ $activeCtx->{terms} }) {
  0            
3379 0 0         println "5 [$term]" if $debug;
3380 0           my $tdef = $self->_ctx_term_defn($activeCtx, $term);
3381 0           my $iri_mapping = $tdef->{'iri_mapping'};
3382 0           my $iri_is_prefix = _is_prefix_of($iri_mapping, $var);
3383            
3384 0 0 0       if (not(defined($tdef)) or (($iri_mapping // '') eq $var) or not($iri_is_prefix) or (not(exists $tdef->{'prefix_flag'}) or not($tdef->{'prefix_flag'}))) {
      0        
      0        
      0        
      0        
3385 0 0         println "5.1" if $debug;
3386 0           next;
3387             }
3388              
3389 0 0         println "5.2" if $debug;
3390 0           my $candidate = join(':', $term, substr($var, length($iri_mapping)));
3391            
3392 0 0         println "5.3 candidate = $candidate" if $debug;
3393 0 0         if (not defined($compact_iri)) {
3394 0 0         println "5.3a" if $debug;
3395 0           $compact_iri = $candidate;
3396             } else {
3397 0           my $shorter = (length($candidate) < length($compact_iri));
3398 0   0       my $less = ((length($candidate) == length($compact_iri)) and (($candidate cmp $compact_iri) == -1));
3399 0           my $cand_tdef = $self->_ctx_term_defn($activeCtx, $candidate);
3400 0           my $no_tdef = not($cand_tdef);
3401 0 0         println "5.3b CANDIDATE: $candidate" if $debug;
3402 0 0 0       if (($shorter or $less) and $no_tdef) {
    0 0        
      0        
      0        
3403 0 0         println "5.3b" if $debug;
3404 0           $compact_iri = $candidate;
3405             } elsif (exists $cand_tdef->{'iri_mapping'} and $cand_tdef->{'iri_mapping'} eq $var and not defined($value)) {
3406 0 0         println "5.3c" if $debug;
3407 0           $compact_iri = $candidate;
3408             }
3409             }
3410             }
3411            
3412 0 0         if (defined($compact_iri)) {
3413 0 0         println "6 returning: $compact_iri" if $debug;
3414 0           return $compact_iri;
3415             }
3416            
3417 0           my $iri = eval { IRI->new($var) };
  0            
3418 0 0         unless ($@) {
3419 0           my $scheme = $iri->scheme;
3420 0           my $scheme_tdef = $self->_ctx_term_defn($activeCtx, $scheme);
3421 0 0 0       if ($scheme_tdef and $scheme_tdef->{'prefix_flag'} and substr($var, length($scheme)+1, 2) ne '//') {
      0        
3422 0 0         println "7" if $debug;
3423 0           die 'IRI confused with prefix';
3424             }
3425             }
3426              
3427 0 0         if (not $vocab) {
3428 0 0         println "8" if $debug;
3429 0 0         my $base = eval { (exists $activeCtx->{'@base'}) ? IRI->new($activeCtx->{'@base'}) : $self->base_iri };
  0            
3430 0 0         unless ($@) {
3431 0           eval {
3432 0           my $iri = IRI->new($var);
3433 0           $var = _make_relative_iri($base, $iri);
3434             };
3435             }
3436             }
3437              
3438 0 0         println "9 returning: $var" if $debug;
3439 0           return $var;
3440             }
3441            
3442             sub _6_3_value_compaction {
3443 0     0     my $self = shift;
3444 0           my $activeCtx = shift;
3445 0           my $inverseCtx = shift;
3446 0           my $activeProp = shift;
3447 0           my $value = shift;
3448             {
3449 2     2   26 no warnings 'uninitialized';
  2         3  
  2         3488  
  0            
3450 0 0         println "ENTER =================> _6_3_value_compaction('$activeProp')" if $debug;
3451             }
3452 0           my $__indent = indent();
3453 0 0         println(Data::Dumper->Dump([$value], [qw(value)])) if $debug;
3454            
3455 0 0         println "1" if $debug;
3456 0           my $result = clone($value); # https://github.com/w3c/json-ld-api/issues/350
3457            
3458 0 0         println "2" if $debug;
3459 0           my $tdef = $self->_ctx_term_defn($activeCtx, $activeProp);
3460             my $language = exists $tdef->{'language_mapping'}
3461             ? $tdef->{'language_mapping'}
3462 0 0         : $activeCtx->{'@language'};
3463            
3464 0 0         println "3" if $debug;
3465             my $direction = exists $tdef->{'direction_mapping'}
3466             ? $tdef->{'direction_mapping'}
3467 0 0         : $activeCtx->{'@direction'};
3468              
3469 0 0         my @keys = grep { $_ ne '@id' and $_ ne '@index' } keys %$value;
  0            
3470 0   0       my $type_mapping = $tdef->{'type_mapping'} // '';
3471 0           my $container_mapping = $tdef->{'container_mapping'};
3472 0 0 0       if (exists $value->{'@id'} and scalar(@keys) == 0) {
    0 0        
    0 0        
    0 0        
3473 0 0         println "4" if $debug;
3474 0 0         if ($type_mapping eq '@id') {
    0          
3475 0 0         println "4.1" if $debug;
3476 0           $result = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $value->{'@id'});
3477             } elsif ($type_mapping eq '@vocab') {
3478 0 0         println "4.2" if $debug;
3479 0           $result = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $value->{'@id'}, vocab => 1);
3480             }
3481             } elsif (exists $value->{'@type'} and $value->{'@type'} eq $type_mapping) {
3482 0 0         println "5" if $debug;
3483 0           $result = $value->{'@value'};
3484             } elsif ($type_mapping eq '@none' or (exists $value->{'@type'} and $value->{'@type'} ne $type_mapping)) {
3485 0 0         println "6" if $debug;
3486 0 0         if (exists $result->{'@type'}) {
3487 0 0         println "6.1" if $debug;
3488 0 0         my @types = (ref($value->{'@type'}) eq 'ARRAY') ? @{ $value->{'@type'} } : $value->{'@type'};
  0            
3489 0           $result->{'@type'} = [ map { $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $_, vocab => 1) } @types ];
  0            
3490             }
3491             } elsif (not _is_string($value->{'@value'})) {
3492 0 0         println "7" if $debug;
3493 0           my @keys = keys %$value;
3494 0 0 0       if (scalar(@keys) == 1 and $keys[0] eq '@value') {
    0 0        
3495             # https://github.com/w3c/json-ld-api/issues/351
3496 0           $result = $value->{'@value'};
3497             } elsif (exists $value->{'@index'} and $self->_cm_contains($container_mapping, '@index')) {
3498 0 0         println "7.1" if $debug;
3499 0           $result = $value->{'@value'};
3500             }
3501             } else {
3502             # https://github.com/w3c/json-ld-api/issues/313
3503 0 0         println "maybe 8" if $debug;
3504 0   0       my $vlang = $value->{'@language'} // '';
3505 0   0       my $lang_both_undef = (not(defined($value->{'@language'})) and not(defined($language)));
3506 0   0       my $lang_same = ($lang_both_undef or (defined($vlang) and defined($language) and lc($vlang) eq lc($language)));
3507 0   0       my $lang_not_present = ((not defined($language)) and (not exists $value->{'@language'}));
3508 0           my $dir = $value->{'@direction'};
3509 0   0       my $dir_both_undef = (not(defined($value->{'@direction'})) and not(defined($direction)));
3510 0   0       my $dir_same = ($dir_both_undef or ($dir eq $direction));
3511 0   0       my $dir_not_present = ((not defined($direction)) and (not exists $value->{'@direction'}));
3512            
3513             # if (($lang_same or $lang_not_present) and ($dir_same or $dir_not_present)) {
3514             # println "8" if $debug;
3515             # if ((exists $value->{'@index'} and $self->_cm_contains($container_mapping, '@index')) or not exists $value->{'@index'}) {
3516             # println "8.1" if $debug;
3517             # $result = $value->{'@value'};
3518             # }
3519             # }
3520            
3521             my $lang_cmp_1 = (defined($language))
3522             ? lc($value->{'@language'}) eq $language
3523 0 0         : not exists($value->{'@language'});
3524             my $dir_cmp_1 = (defined($direction))
3525             ? $value->{'@direction'} eq $direction
3526 0 0         : not exists($value->{'@direction'});
3527 0 0 0       if ($lang_cmp_1 and $dir_cmp_1) {
3528 0 0         println "8" if $debug;
3529 0 0 0       if ((exists $value->{'@index'} and $self->_cm_contains($container_mapping, '@index')) or not exists $value->{'@index'}) {
      0        
3530 0 0         println "8.1" if $debug;
3531 0           $result = $value->{'@value'};
3532             }
3533             }
3534            
3535 0 0         println "not 8" if $debug;
3536 0 0         println(Data::Dumper->Dump([$lang_cmp_1, $language, $dir_cmp_1, $direction, $value], [qw(lang_cmp_1 language dir_cmp_1 direction value)])) if $debug;
3537             }
3538              
3539 0 0         if (ref($result) eq 'HASH') {
3540 0 0         println "9" if $debug;
3541 0           my @keys = keys %$result;
3542 0           foreach my $k (@keys) {
3543 0           my $__indent = indent();
3544 0 0         println "----------------" if $debug;
3545 0 0         println "9 [$k]" if $debug;
3546 0           my $ck = $self->_6_2_2_iri_compaction($activeCtx, $inverseCtx, $k, vocab => 1);
3547 0           $result->{$ck} = delete $result->{$k};
3548             }
3549             }
3550            
3551 0 0         println "10" if $debug;
3552 0           return $result;
3553             }
3554            
3555             sub _7_1_2_flattening {
3556 0     0     my $self = shift;
3557 0           my $element = shift;
3558 0           my $context = shift;
3559 0   0       my $ordered = shift // 0;
3560 0           die;
3561             }
3562              
3563             sub _7_2_2_nodemap_generation {
3564 0 0   0     println "ENTER =================> _7_2_2_nodemap_generation" if $debug;
3565 0           my $__indent = indent();
3566 0           my $self = shift;
3567 0           my $element = shift;
3568 0           my $map = shift;
3569 0   0       my $activeGraph = shift // '@default';
3570 0 0         println(Data::Dumper->Dump([$activeGraph], [qw(activeGraph)])) if $debug;
3571 0 0         println(Data::Dumper->Dump([$element], [qw(element)])) if $debug;
3572 0           my $activeSubject = shift;
3573 0           my $activeProp = shift;
3574 0           my $list = shift;
3575            
3576 0 0         println "1" if $debug;
3577 0 0         if (ref($element) eq 'ARRAY') {
3578 0           foreach my $item (@$element) {
3579 0           $self->_7_2_2_nodemap_generation($item, $map, $activeGraph, $activeSubject, $activeProp, $list);
3580             }
3581 0           return;
3582             }
3583            
3584 0 0         println "2" if $debug;
3585 0   0       my $graph = ($map->{$activeGraph} ||= {});
3586 0           my $node;
3587             my $subjectNode;
3588 0 0         if (not defined($activeSubject)) {
3589 0           $node = undef;
3590             } else {
3591 0           $subjectNode = $graph->{$activeSubject};
3592             }
3593            
3594 0 0         unless (ref($element) eq 'HASH') {
3595 0           Carp::cluck 'element is not a HASH';
3596             }
3597            
3598 0 0         if (exists $element->{'@type'}) {
3599 0 0         println "3" if $debug;
3600             # https://github.com/w3c/json-ld-api/issues/276
3601 0 0 0       if (ref($element) and ref($element) ne 'HASH') {
3602 0           Carp::cluck 'element is not a HASH';
3603             }
3604 0 0         if (ref($element->{'@type'})) {
3605 0           foreach my $i (0 .. $#{ $element->{'@type'} }) {
  0            
3606 0           my $item = $element->{'@type'}[$i];
3607 0 0         if ($item =~ /^_:/) {
3608 0 0         println "3.1" if $debug;
3609 0           $element->{'@type'}[$i] = $self->_7_4_2_generate_blank_node_ident($item);
3610             }
3611             }
3612             } else {
3613 0 0         if ($element->{'@type'} =~ /^_:/) {
3614 0           $element->{'@type'} = $self->_7_4_2_generate_blank_node_ident($element->{'@type'});
3615             }
3616             }
3617             }
3618            
3619 0           my $j = JSON->new()->canonical(1);
3620 0 0         if (exists $element->{'@value'}) {
    0          
3621 0 0         println "4" if $debug;
3622 0 0         if (not defined($list)) {
3623 0 0         println "4.1" if $debug;
3624 2     2   18 no warnings 'uninitialized';
  2         6  
  2         9817  
3625 0 0         if (not exists $subjectNode->{$activeProp}) {
3626 0 0         println "4.1.1" if $debug;
3627 0           $subjectNode->{$activeProp} = [$element];
3628             } else {
3629 0 0         println "4.1.2" if $debug;
3630 0           my $je = $j->encode($element);
3631 0     0     my $exists = any { $j->encode($_) eq $je } @{ $subjectNode->{$activeProp} };
  0            
  0            
3632 0 0         unless ($exists) {
3633 0           push(@{ $subjectNode->{$activeProp} }, $element);
  0            
3634             }
3635             }
3636             } else {
3637 0 0         println "4.2" if $debug;
3638 0           push(@{ $list->{'@list'} }, $element);
  0            
3639             }
3640             } elsif (exists $element->{'@list'}) {
3641 0 0         println "5" if $debug;
3642 0 0         println "5.1" if $debug;
3643 0           my $result = {'@list' => []};
3644            
3645 0 0         println "5.2" if $debug;
3646 0           $self->_7_2_2_nodemap_generation($element->{'@list'}, $map, $activeGraph, $activeSubject, $activeProp, $result);
3647            
3648 0 0         if (not defined($list)) {
3649 0 0         println "5.3" if $debug;
3650 0 0         println(Data::Dumper->Dump([$result], [qw(result)])) if $debug;
3651 0           push(@{ $subjectNode->{$activeProp} }, $result);
  0            
3652             } else {
3653 0 0         println "5.4" if $debug;
3654 0           push(@{ $list->{'@list'} }, $result);
  0            
3655             }
3656             } else {
3657 0 0         println "6" if $debug;
3658 0           my $id;
3659 0 0         if (exists $element->{'@id'}) {
3660 0 0         println "6.1" if $debug;
3661 0           $id = delete $element->{'@id'};
3662 0 0         if ($id =~ /^_:/) {
3663 0           $id = $self->_7_4_2_generate_blank_node_ident($id);
3664             }
3665             } else {
3666 0 0         println "6.2" if $debug;
3667 0           $id = $self->_7_4_2_generate_blank_node_ident();
3668             }
3669            
3670 0 0         unless (exists $graph->{$id}) {
3671 0 0         println "6.3" if $debug;
3672 0           $graph->{$id} = { '@id' => $id };
3673             }
3674            
3675 0 0         println "6.4" if $debug;
3676 0           $node = $graph->{$id};
3677            
3678 0 0         if (ref($activeSubject) eq 'HASH') {
    0          
3679 0 0         println "6.5" if $debug;
3680 0 0         if (not exists $node->{$activeProp}) {
3681 0 0         println "6.5.1" if $debug;
3682 0           $node->{$activeProp} = [$activeSubject];
3683             } else {
3684 0 0         println "6.5.2" if $debug;
3685 0           my $je = $j->encode($activeSubject);
3686 0     0     my $exists = any { $j->encode($_) eq $je } @{ $node->{$activeProp} };
  0            
  0            
3687 0 0         unless ($exists) {
3688 0           push(@{ $node->{$activeProp} }, $activeSubject);
  0            
3689             }
3690             }
3691             } elsif (defined($activeProp)) {
3692 0 0         println "6.6" if $debug;
3693 0 0         println "6.6.1" if $debug;
3694 0           my $reference = { '@id' => $id };
3695            
3696 0 0         if (not defined($list)) {
3697 0 0         println "6.6.2" if $debug;
3698 0 0         if (not exists $subjectNode->{$activeProp}) {
3699 0 0         println "6.6.2.1" if $debug;
3700 0           $subjectNode->{$activeProp} = [$reference];
3701             } else {
3702 0 0         println "6.6.2.2" if $debug;
3703 0           my $je = $j->encode($reference);
3704 0     0     my $exists = any { $j->encode($_) eq $je } @{ $subjectNode->{$activeProp} };
  0            
  0            
3705 0 0         unless ($exists) {
3706 0           push(@{ $subjectNode->{$activeProp} }, $reference);
  0            
3707             }
3708             }
3709             } else {
3710 0           push(@{ $list->{'@list'} }, $reference);
  0            
3711             }
3712             }
3713            
3714 0 0         if (exists $element->{'@type'}) {
3715 0 0         println "6.7" if $debug;
3716 0           my %exists = map { $_ => 1 } @{ $node->{'@type'} };
  0            
  0            
3717 0           push(@{ $node->{'@type'} }, grep { not exists $exists{$_} }@{ $element->{'@type'} });
  0            
  0            
  0            
3718 0           delete $element->{'@type'};
3719             }
3720            
3721 0 0         if (exists $element->{'@index'}) {
3722 0 0         println "6.8" if $debug;
3723 0 0         println "6.8 TODO check if element has a pre-existing value" if $debug;
3724 0           $node->{'@index'} = delete $element->{'@index'};
3725             }
3726              
3727 0 0         if (exists $element->{'@reverse'}) {
3728 0 0         println "6.9" if $debug;
3729 0 0         println "6.9.1" if $debug;
3730 0           my $referenced_node = { '@id' => $id };
3731            
3732 0 0         println "6.9.2" if $debug;
3733 0           my $reverse_map = $element->{'@reverse'};
3734            
3735 0 0         println "6.9.3" if $debug;
3736 0           foreach my $property (keys %$reverse_map) {
3737 0 0         println "6.9.3 [$property]" if $debug;
3738 0           my $values = $reverse_map->{$property};
3739            
3740 0 0         println "6.9.3.1" if $debug;
3741 0           foreach my $value (@$values) {
3742 0 0         println "6.9.3.1.1" if $debug;
3743 0           $self->_7_2_2_nodemap_generation($value, $map, $activeGraph, $referenced_node, $property); # TODO: need to pass $list ?
3744             }
3745             }
3746              
3747 0 0         println "6.9.3.4" if $debug;
3748 0           delete $element->{'@reverse'};
3749             }
3750            
3751 0 0         if (exists $element->{'@graph'}) {
3752 0 0         println "6.10" if $debug;
3753 0           $self->_7_2_2_nodemap_generation($element->{'@graph'}, $map, $id);
3754 0           delete $element->{'@graph'};
3755             }
3756            
3757 0 0         if (exists $element->{'@included'}) {
3758 0 0         println "6.11" if $debug;
3759 0           $self->_7_2_2_nodemap_generation($element->{'@included'}, $map, $activeGraph);
3760 0           delete $element->{'@included'};
3761             }
3762              
3763 0 0         println "6.12" if $debug;
3764 0           foreach my $property (sort keys %$element) {
3765 0 0         println '----------------' if $debug;
3766 0 0         println "6.12 [$property]" if $debug;
3767 0           my $value = $element->{$property};
3768 0 0         if ($property =~ /^_:/) {
3769 0 0         println "6.12.1" if $debug;
3770 0           $property = $self->_7_4_2_generate_blank_node_ident($property);
3771             }
3772 0 0         unless (exists $node->{$property}) {
3773 0 0         println "6.12.2" if $debug;
3774 0           $node->{$property} = [];
3775             }
3776            
3777 0 0         println "6.12.3" if $debug;
3778 0           $self->_7_2_2_nodemap_generation($value, $map, $activeGraph, $id, $property);
3779             }
3780             }
3781             }
3782              
3783             sub _7_4_2_generate_blank_node_ident {
3784 0 0   0     println "ENTER =================> _7_4_2_generate_blank_node_ident" if $debug;
3785 0           my $__indent = indent();
3786 0           my $self = shift;
3787 0           my $ident = shift;
3788 0 0 0       if (defined($ident) and exists $self->identifier_map->{$ident}) {
3789 0 0         println "1" if $debug;
3790 0           return $self->identifier_map->{$ident};
3791             }
3792            
3793 0 0         println "2" if $debug;
3794 0           my $nid = $self->next_identifier_id;
3795 0           $self->next_identifier_id($nid + 1);
3796 0           my $bid = "_:b$nid";
3797              
3798 0 0         if (defined($ident)) {
3799 0 0         println "3" if $debug;
3800 0           $self->identifier_map->{$ident} = $bid;
3801             }
3802            
3803 0 0         println "4" if $debug;
3804 0           return $bid;
3805             }
3806              
3807             sub _8_1_2_to_rdf {
3808 0 0   0     println "ENTER =================> _8_1_2_to_rdf" if $debug;
3809 0           my $__indent = indent();
3810 0           my $self = shift;
3811 0           my $map = shift;
3812 0           my $dataset = shift;
3813 0           my %args = @_;
3814 0   0       my $produce_genrdf = $args{'produceGeneralizedRdf'} || 0;
3815 0           my $rdfDirection = $args{'rdfDirection'};
3816            
3817 0 0         println "1" if $debug;
3818 0           for my $graphName (keys %$map) {
3819 0           my $__indent = indent();
3820 0 0         println '----------------------' if $debug;
3821 0 0         println "1 [$graphName]" if $debug;
3822 0           my $graph = $map->{$graphName};
3823              
3824 0 0         unless ($self->_is_well_formed_graphname($graphName)) {
3825 0 0         println "1.1" if $debug;
3826 0           next;
3827             }
3828            
3829 0           my $graph_iri;
3830 0 0         println "1.2" if $debug;
3831 0 0         if ($graphName eq '@default') {
3832 0           $graph_iri = $self->default_graph();
3833             } else {
3834 0           $graph_iri = $self->new_graphname($graphName);
3835             }
3836            
3837 0 0         println "1.3" if $debug;
3838 0           foreach my $subject (sort keys %$graph) {
3839 0           my $__indent = indent();
3840 0 0         println '----------------------' if $debug;
3841 0 0         println "1.3 [$subject]" if $debug;
3842 0           my $node = $graph->{$subject};
3843              
3844 0 0         println "1.3.2" if $debug;
3845 0           foreach my $property (sort keys %$node) {
3846 0 0         println "1.3.2 [$property]" if $debug;
3847 0           my $values = $node->{$property};
3848 0 0         println(Dumper($property, $node)) if $debug;
3849 0 0 0       if ($property eq '@type') {
    0          
    0          
    0          
3850 0 0         println "1.3.2.1" if $debug;
3851 0           foreach my $type (@$values) {
3852 0 0         if ($self->_is_well_formed_graph_node($type)) {
3853 0           my $q = $self->new_quad(
3854             $self->new_graph_node($subject),
3855             $self->new_iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
3856             $self->new_graph_node($type),
3857             $graph_iri
3858             );
3859 0           $self->add_quad($q, $dataset);
3860             }
3861             }
3862             } elsif (exists $keywords{$property}) {
3863 0 0         println "1.3.2.2" if $debug;
3864 0           next;
3865             } elsif ($property =~ /^_:(.*)$/ and not $produce_genrdf) {
3866 0 0         println "1.3.2.3" if $debug;
3867 0           next;
3868             } elsif (not $self->_is_well_formed_iri($property)) {
3869 0 0         println "1.3.2.4" if $debug;
3870 0           next;
3871             } else {
3872 0 0         println "1.3.2.5" if $debug;
3873 0           foreach my $item (@$values) {
3874 0 0         println "1.3.2.5.1" if $debug;
3875 0 0         println "1.3.2.5.2" if $debug;
3876 0           my $list_triples = [];
3877 0           my $s = $self->_8_2_2_object_to_rdf({'@id' => $subject}, $list_triples);
3878 0           my $o = $self->_8_2_2_object_to_rdf($item, $list_triples);
3879 0 0         if ($o) {
3880 0 0         println "1.3.2.5.3" if $debug;
3881 0           my $q = $self->new_quad(
3882             $s,
3883             $self->new_iri($property),
3884             $o,
3885             $graph_iri
3886             );
3887 0 0         if ($q) {
3888 0           $self->add_quad($q, $dataset);
3889             }
3890             }
3891 0 0         println "1.3.2.5.4" if $debug;
3892 0           foreach my $t (@$list_triples) {
3893 0           my $q = $t->as_quad($graph_iri);
3894 0           $self->add_quad($q, $dataset);
3895             }
3896             }
3897             }
3898             }
3899             }
3900             }
3901             }
3902              
3903             sub _8_2_2_object_to_rdf {
3904 0 0   0     println "ENTER =================> _8_2_2_object_to_rdf" if $debug;
3905 0           my $__indent = indent();
3906 0           my $self = shift;
3907 0           my $item = shift;
3908 0           my $list_triples = shift;
3909 0 0 0       if ($self->_is_node_object($item) and not $self->_is_well_formed_graph_node($item->{'@id'})) {
3910 0 0         println "1" if $debug;
3911 0 0         println "*** _8_2_2_object_to_rdf RETURNING NULL on " . Data::Dumper->Dump([$item], ['item']) if $debug;
3912 0           return;
3913             }
3914              
3915 0 0         if ($self->_is_node_object($item)) {
3916 0           my $value = $item->{'@id'};
3917 0 0         println "2 $value" if $debug;
3918 0 0         if ($value =~ /^_:(.*)$/) {
3919 0           return $self->new_blank($1);
3920             } else {
3921 0           return $self->new_iri($value);
3922             }
3923             }
3924            
3925 0 0         if ($self->_is_list_object($item)) {
3926 0 0         println "3" if $debug;
3927 0           return $self->_8_3_2_list_conversion($item->{'@list'}, $list_triples);
3928             }
3929            
3930 0 0         println "4" if $debug;
3931 0           my $value = $item->{'@value'};
3932            
3933 0 0         println "5" if $debug;
3934 0           my $datatype = $item->{'@type'};
3935              
3936 0 0 0       if (defined($datatype) and not $self->_is_well_formed_datatype($datatype)) { # https://github.com/w3c/json-ld-api/issues/282 ; https://github.com/w3c/json-ld-api/issues/298
3937 0 0         println "6" if $debug;
3938 0 0         println "*** _8_2_2_object_to_rdf RETURNING NULL" if $debug;
3939 0           return;
3940             }
3941            
3942 0 0 0       if (exists $item->{'@language'} and not $self->_is_well_formed_language($item->{'@language'})) {
3943 0 0         println "7" if $debug;
3944 0 0         println "*** _8_2_2_object_to_rdf RETURNING NULL" if $debug;
3945 0           return;
3946             }
3947            
3948 0 0 0       if (defined($datatype) and $datatype eq '@json') {
3949 0 0         println "8" if $debug;
3950 0           $value = decode_utf8(JSON->new->utf8->allow_nonref->canonical(1)->encode($value));
3951 0           $datatype = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON';
3952             }
3953            
3954 0 0         if (JSON::is_bool($value)) {
3955 0 0         println "9" if $debug;
3956 0 0         $value = $value ? 'true' : 'false';
3957 0 0         unless ($datatype) {
3958 0           $datatype = 'http://www.w3.org/2001/XMLSchema#boolean';
3959             }
3960             }
3961            
3962 0           my $is_num = _is_numeric($value);
3963 0           my $is_int = _is_integer($value);
3964 0 0         my $int_repr = $is_num ? sprintf('%011.0f', $value) : '';
3965 0   0       my $double_or_not_int = (not($is_int) or (defined($datatype) and $datatype eq 'http://www.w3.org/2001/XMLSchema#double'));
3966 0   0       my $is_large_int = ($is_int and length($int_repr) >= 22 and $int_repr =~ /^[^-+0]/);
3967 0 0 0       if ($is_large_int or ($is_num and $double_or_not_int)) {
    0 0        
3968             # https://github.com/w3c/json-ld-api/issues/312
3969 0 0         println "10" if $debug;
3970 0           $value = sprintf('%E', $value);
3971 0           $value =~ s/0+E/0E/;
3972 0           $value =~ s/(\d)0E/$1E/;
3973 0           $value =~ s/E[+]/E/;
3974 0           $value =~ s/E0+([1-9])/E$1/;
3975 0           $value =~ s/E0+/E0/;
3976 0 0         unless ($datatype) {
3977 0           $datatype = 'http://www.w3.org/2001/XMLSchema#double';
3978             }
3979             } elsif ($is_num) {
3980 0 0         println "11" if $debug;
3981 0           $value = sprintf('%011.0f', $value);
3982 0           $value =~ s/^(-?)0+(\d)/$1$2/;
3983 0           $value =~ s/^-0/0/;
3984            
3985 0 0         unless ($datatype) {
3986 0           $datatype = 'http://www.w3.org/2001/XMLSchema#integer';
3987             }
3988             }
3989              
3990 0 0         if (not defined($datatype)) {
3991 0 0         println "12" if $debug;
3992 0 0         $datatype = (exists $item->{'@language'}) ? 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString' : 'http://www.w3.org/2001/XMLSchema#string';
3993             }
3994            
3995 0           my $literal;
3996 0 0 0       if (exists $item->{'@direction'} and defined(my $dir = $self->rdf_direction)) {
3997 0 0         println "13" if $debug;
3998 0 0         if ($dir eq 'i18n-datatype') {
    0          
3999 0 0         println "13.1" if $debug;
4000 0   0       my $language = $item->{'@language'} // '';
4001             # https://github.com/w3c/json-ld-api/issues/337
4002 0           $datatype = 'https://www.w3.org/ns/i18n#' . join('_', lc($language), $item->{'@direction'});
4003 0           $literal = $self->new_dt_literal($value, $datatype);
4004             } elsif ($dir eq 'compound-literal') {
4005 0 0         println "13.2" if $debug;
4006 0 0         println "13.2.1" if $debug;
4007 0           $literal = $self->new_blank();
4008 0 0         println "13.2.2" if $debug;
4009 0           println "TODO: spec text issue for literal construction";
4010             my $t = $self->new_triple(
4011             $literal,
4012             $self->new_iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#value'),
4013 0           $self->new_dt_literal($item->{'@value'}, 'http://www.w3.org/2001/XMLSchema#string')
4014             # $item->{'@value'} # TODO: should this be a term constructor call?
4015             );
4016 0           push(@$list_triples, $t);
4017            
4018 0 0         if (exists $item->{'@language'}) {
4019 0 0         println "13.2.3" if $debug;
4020 0           println "TODO: spec text issue for literal construction";
4021             my $t = $self->new_triple(
4022             $literal,
4023             $self->new_iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#language'),
4024 0           $self->new_dt_literal(lc($item->{'@language'}), 'http://www.w3.org/2001/XMLSchema#string')
4025             # $item->{'@language'} # TODO: should this be a term constructor call?
4026             );
4027 0           push(@$list_triples, $t);
4028             }
4029            
4030 0 0         println "13.2.4" if $debug;
4031 0           println "TODO: spec text issue for literal construction";
4032             my $t2 = $self->new_triple(
4033             $literal,
4034             $self->new_iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#direction'),
4035 0           $self->new_dt_literal($item->{'@direction'}, 'http://www.w3.org/2001/XMLSchema#string')
4036             # $item->{'@direction'} # TODO: should this be a term constructor call?
4037             );
4038 0           push(@$list_triples, $t2);
4039             }
4040             } else {
4041 0 0         println "14" if $debug;
4042             $literal = (exists $item->{'@language'})
4043 0 0 0       ? $self->new_lang_literal($value, lc($item->{'@language'} // ''))
4044             : $self->new_dt_literal($value, $datatype);
4045             }
4046            
4047 0 0         println "15" if $debug;
4048 0           return $literal;
4049             }
4050              
4051             sub _8_3_2_list_conversion {
4052 0 0   0     println "ENTER =================> _8_3_2_list_conversion" if $debug;
4053 0           my $__indent = indent();
4054 0           my $self = shift;
4055 0           my $list = shift;
4056 0           my $list_triples = shift;
4057            
4058 0           my $NIL = $self->new_iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil');
4059 0 0         if (scalar(@$list) == 0) {
4060 0 0         println "1" if $debug;
4061 0           return $NIL;
4062             }
4063            
4064 0 0         println "2" if $debug;
4065 0           my $bnodes = [map { $self->new_blank() } @$list];
  0            
4066 0           foreach my $i (0..$#{$bnodes}) {
  0            
4067 0           my $subject = $bnodes->[$i];
4068 0           my $item = $list->[$i];
4069 0 0         println "3" if $debug;
4070 0 0         println "3.1" if $debug;
4071 0           my $embedded_triples = [];
4072            
4073 0 0         println "3.2" if $debug;
4074 0           my $object = $self->_8_2_2_object_to_rdf($item, $embedded_triples);
4075            
4076 0 0         if (defined($object)) {
4077 0 0         println "3.3" if $debug;
4078 0           push(@$list_triples, $self->new_triple(
4079             $subject,
4080             $self->new_iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#first'),
4081             $object
4082             ));
4083             }
4084            
4085 0 0         println "3.4" if $debug;
4086 0 0         my $rest = ($i == $#{$bnodes}) ? $NIL : $bnodes->[$i+1];
  0            
4087 0           push(@$list_triples, $self->new_triple(
4088             $subject,
4089             $self->new_iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#rest'),
4090             $rest
4091             ));
4092            
4093 0 0         println "3.5" if $debug;
4094 0           push(@$list_triples, @$embedded_triples);
4095             }
4096              
4097 0 0         println "4" if $debug;
4098 0 0         my $head = (scalar(@$bnodes)) ? $bnodes->[0] : $NIL;
4099 0           return $head;
4100             }
4101            
4102             sub new_graph_node {
4103 0     0 0   my $self = shift;
4104 0           my $value = shift;
4105 0 0         if ($value =~ /_:(.*)/) {
4106 0           return $self->new_blank($1);
4107             } else {
4108 0           return $self->new_iri($value);
4109             }
4110             }
4111              
4112             sub new_graphname {
4113 0     0 1   my $self = shift;
4114 0           Carp::cluck 'RDF term constructors must be overloaded in a JSONLD subclass';
4115             }
4116              
4117             sub default_graph {
4118 0     0 1   my $self = shift;
4119 0           Carp::cluck 'RDF default graph accessor must be overloaded in a JSONLD subclass';
4120             }
4121            
4122             sub new_dataset {
4123 0     0 1   my $self = shift;
4124 0           Carp::cluck 'RDF dataset constructors must be overloaded in a JSONLD subclass';
4125             }
4126            
4127             sub new_triple {
4128 0     0 1   my $self = shift;
4129 0           Carp::cluck 'RDF statement constructors must be overloaded in a JSONLD subclass';
4130             }
4131            
4132             sub new_quad {
4133 0     0 1   my $self = shift;
4134 0           Carp::cluck 'RDF statement constructors must be overloaded in a JSONLD subclass';
4135             }
4136            
4137             sub new_iri {
4138 0     0 1   my $self = shift;
4139 0           Carp::cluck 'RDF term constructors must be overloaded in a JSONLD subclass';
4140             }
4141            
4142             sub new_blank {
4143 0     0 1   my $self = shift;
4144 0           Carp::cluck 'RDF term constructors must be overloaded in a JSONLD subclass';
4145             }
4146            
4147             sub new_lang_literal {
4148 0     0 1   my $self = shift;
4149 0           Carp::cluck 'RDF term constructors must be overloaded in a JSONLD subclass';
4150             }
4151            
4152             sub new_dt_literal {
4153 0     0 1   my $self = shift;
4154 0           Carp::cluck 'RDF term constructors must be overloaded in a JSONLD subclass';
4155             }
4156            
4157             sub add_quad {
4158 0     0 1   my $self = shift;
4159 0           Carp::cluck 'RDF dataset methods must be overloaded in a JSONLD subclass';
4160             }
4161             }
4162              
4163              
4164              
4165             1;
4166              
4167             __END__
4168              
4169             =back
4170              
4171             =head1 BUGS
4172              
4173             Please report any bugs or feature requests to through the GitHub web interface
4174             at L<https://github.com/kasei/perl-jsonld/issues>.
4175              
4176             =head1 SEE ALSO
4177              
4178             =over 4
4179              
4180             =item L<irc://irc.perl.org/#perlrdf>
4181              
4182             =item L<AtteanX::Parser::JSONLD>
4183              
4184             =item L<https://www.w3.org/TR/json-ld11/>
4185              
4186             =item L<https://www.w3.org/TR/json-ld-api/>
4187              
4188             =item L<MooX::Role::JSON_LD>
4189              
4190             =back
4191              
4192             =head1 AUTHOR
4193              
4194             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
4195              
4196             =head1 COPYRIGHT
4197              
4198             Copyright (c) 2019--2020 Gregory Todd Williams.
4199             This program is free software; you can redistribute it and/or modify it under
4200             the same terms as Perl itself.
4201              
4202             =cut