@@ -78,6 +78,14 @@ class TermDefinition
78
78
# @return [Boolean]
79
79
attr_writer :protected
80
80
81
+ # Previous definition defined for this term. This is used for rolling back term definitions based on scoped types.
82
+ # @return [TermDefinition]
83
+ attr_reader :previous_definition
84
+
85
+ # Definition is from a type-scoped context.
86
+ # @return [TermDefinition]
87
+ attr_reader :type_scoped
88
+
81
89
# This is a simple term definition, not an expanded term definition
82
90
# @return [Boolean] simple
83
91
def simple? ; simple ; end
@@ -90,6 +98,8 @@ def prefix?; @prefix; end
90
98
# @param [String] term
91
99
# @param [String] id
92
100
# @param [String] type_mapping Type mapping
101
+ # @param [Boolean] type_scoped
102
+ # @param [TermDefinition] previous_definition
93
103
# @param [Array<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] container_mapping
94
104
# @param [String] language_mapping
95
105
# Language mapping of term, `false` is used if there is explicitly no language mapping for this term
@@ -104,6 +114,8 @@ def initialize(term,
104
114
id : nil ,
105
115
index : nil ,
106
116
type_mapping : nil ,
117
+ previous_definition : nil ,
118
+ type_scoped : false ,
107
119
container_mapping : nil ,
108
120
language_mapping : nil ,
109
121
reverse_property : false ,
@@ -116,6 +128,8 @@ def initialize(term,
116
128
@id = id . to_s unless id . nil?
117
129
@index = index . to_s unless index . nil?
118
130
@type_mapping = type_mapping . to_s unless type_mapping . nil?
131
+ @previous_definition = previous_definition
132
+ @type_scoped = type_scoped
119
133
self . container_mapping = container_mapping
120
134
@language_mapping = language_mapping unless language_mapping . nil?
121
135
@reverse_property = reverse_property
@@ -241,6 +255,10 @@ def inspect
241
255
# @return [Hash{String => TermDefinition}]
242
256
attr_reader :term_definitions
243
257
258
+ # Context has definitions defined from type scopes
259
+ # @return [Boolean]
260
+ attr_accessor :has_typed_scopes
261
+
244
262
# @return [Hash{RDF::URI => String}] Reverse mappings from IRI to term only for terms, not CURIEs XXX
245
263
attr_accessor :iri_to_term
246
264
@@ -279,8 +297,8 @@ def inspect
279
297
# @raise [JsonLdError]
280
298
# on a remote context load error, syntax error, or a reference to a term which is not defined.
281
299
# @return [Context]
282
- def self . parse ( local_context , from_term : nil , **options )
283
- self . new ( options ) . parse ( local_context , from_term : from_term )
300
+ def self . parse ( local_context , from_property : false , from_type : false , **options )
301
+ self . new ( options ) . parse ( local_context , from_property : from_property , from_type : from_type )
284
302
end
285
303
286
304
##
@@ -409,15 +427,16 @@ def vocab=(value)
409
427
#
410
428
# @param [String, #read, Array, Hash, Context] local_context
411
429
# @param [Array<String>] remote_contexts
412
- # @param [String] from_term
413
- # The active term, when expanding. Sealed terms may not be cleared unless from a
414
- # context associated with a term used as a property.
430
+ # @param [Boolean] from_property
431
+ # Context is created from a scoped context for a property. Sealed terms may not be cleared unless from a context associated with a term used as a property.
432
+ # @param [Boolean] from_type
433
+ # Context is created from a scoped context for a type. Retains any previously defined term, which can be rolled back when the type context changes.
415
434
# @param [RDF::Resource] context_id from context IRI, for sealing terms
416
435
# @raise [JsonLdError]
417
436
# on a remote context load error, syntax error, or a reference to a term which is not defined.
418
437
# @return [Context]
419
438
# @see https://www.w3.org/TR/json-ld11-api/index.html#context-processing-algorithm
420
- def parse ( local_context , remote_contexts : [ ] , from_term : nil )
439
+ def parse ( local_context , remote_contexts : [ ] , from_property : false , from_type : false )
421
440
result = self . dup
422
441
result . provided_context = local_context if self . empty?
423
442
@@ -426,8 +445,8 @@ def parse(local_context, remote_contexts: [], from_term: nil)
426
445
local_context . each do |context |
427
446
case context
428
447
when nil
429
- # 3.1 If the `from_term ` is not null, and the active context contains protected terms, an error is raised.
430
- if from_term || result . term_definitions . values . none? ( &:protected? )
448
+ # 3.1 If the `from_property ` is not null, and the active context contains protected terms, an error is raised.
449
+ if from_property || result . term_definitions . values . none? ( &:protected? )
431
450
result = Context . new ( options )
432
451
else
433
452
raise JSON ::LD ::JsonLdError ::InvalidContextNullification ,
@@ -502,7 +521,7 @@ def parse(local_context, remote_contexts: [], from_term: nil)
502
521
end
503
522
504
523
# 3.2.6) Set context to the result of recursively calling this algorithm, passing context no base for active context, context for local context, and remote contexts.
505
- context = context_no_base . parse ( context , remote_contexts : remote_contexts . dup , from_term : from_term )
524
+ context = context_no_base . parse ( context , remote_contexts : remote_contexts . dup , from_property : from_property , from_type : from_type )
506
525
PRELOADED [ context_canon . to_s ] = context
507
526
context . provided_context = result . provided_context
508
527
end
@@ -532,7 +551,8 @@ def parse(local_context, remote_contexts: [], from_term: nil)
532
551
context . each_key do |key |
533
552
# ... where key is not @base, @vocab, @language, or @version
534
553
result . create_term_definition ( context , key , defined ,
535
- from_term : from_term ,
554
+ from_property : from_property ,
555
+ from_type : from_type ,
536
556
protected : context [ '@protected' ] ) unless NON_TERMDEF_KEYS . include? ( key )
537
557
end
538
558
else
@@ -590,14 +610,15 @@ def merge!(context)
590
610
# @param [Hash] local_context
591
611
# @param [String] term
592
612
# @param [Hash] defined
593
- # @param [String] from_term
594
- # The active term, when expanding. Sealed terms may not be cleared unless from a
595
- # context associated with a term used as a property.
613
+ # @param [Boolean] from_property
614
+ # Context is created from a scoped context for a property. Sealed terms may not be cleared unless from a context associated with a term used as a property.
615
+ # @param [Boolean] from_type
616
+ # Context is created from a scoped context for a type. Retains any previously defined term, which can be rolled back when the type context changes.
596
617
# @param [Boolean] protected if true, causes all terms to be marked protected
597
618
# @raise [JsonLdError]
598
619
# Represents a cyclical term dependency
599
620
# @see https://www.w3.org/TR/json-ld11-api/index.html#create-term-definition
600
- def create_term_definition ( local_context , term , defined , from_term : nil , protected : false )
621
+ def create_term_definition ( local_context , term , defined , from_property : false , from_type : false , protected : false )
601
622
# Expand a string value, unless it matches a keyword
602
623
#log_debug("create_term_definition") {"term = #{term.inspect}"}
603
624
@@ -627,22 +648,24 @@ def create_term_definition(local_context, term, defined, from_term: nil, protect
627
648
value = { '@id' => value } if simple_term
628
649
629
650
# Remove any existing term definition for term in active context.
630
- if term_definitions [ term ] && term_definitions [ term ] . protected? && from_term . nil?
651
+ previous_definition = term_definitions [ term ]
652
+ if previous_definition && previous_definition . protected? && !from_property
631
653
raise JSON ::LD ::JsonLdError ::ProtectedTermRedefinition , "Attempt to redefine protected term #{ term } "
632
654
else
633
- term_definitions . delete ( term ) unless term_definitions [ term ]
655
+ term_definitions . delete ( term ) if previous_definition
634
656
end
635
657
636
658
case value
637
659
when nil , ID_NULL_OBJECT
638
660
# If value equals null or value is a JSON object containing the key-value pair (@id-null), then set the term definition in active context to null, set the value associated with defined's key term to true, and return.
639
661
#log_debug("") {"=> nil"}
640
- term_definitions [ term ] = TermDefinition . new ( term )
662
+ term_definitions [ term ] = TermDefinition . new ( term , previous_definition : previous_definition , type_scoped : from_type )
663
+ self . has_typed_scopes ||= from_type
641
664
defined [ term ] = true
642
665
return
643
666
when Hash
644
667
#log_debug("") {"Hash[#{term.inspect}] = #{value.inspect}"}
645
- definition = TermDefinition . new ( term )
668
+ definition = TermDefinition . new ( term , previous_definition : previous_definition , type_scoped : from_type )
646
669
definition . simple = simple_term
647
670
648
671
if options [ :validate ]
@@ -774,7 +797,7 @@ def create_term_definition(local_context, term, defined, from_term: nil, protect
774
797
775
798
if value . has_key? ( '@context' )
776
799
begin
777
- self . parse ( value [ '@context' ] , from_term : term )
800
+ self . parse ( value [ '@context' ] , from_property : true )
778
801
# Record null context in array form
779
802
definition . context = value [ '@context' ] ? value [ '@context' ] : [ nil ]
780
803
rescue JsonLdError => e
@@ -809,6 +832,7 @@ def create_term_definition(local_context, term, defined, from_term: nil, protect
809
832
end
810
833
811
834
term_definitions [ term ] = definition
835
+ self . has_typed_scopes ||= from_type
812
836
defined [ term ] = true
813
837
else
814
838
raise JsonLdError ::InvalidTermDefinition , "Term definition for #{ term . inspect } is an #{ value . class } on term #{ term . inspect } "
@@ -934,6 +958,27 @@ def from_vocabulary(graph)
934
958
self
935
959
end
936
960
961
+ ##
962
+ # Revert any type-scoped terms in this context to their previous mappings.
963
+ #
964
+ # Creates a clone of the context with terms associated with type-scoped contexts reverted to their prior state
965
+ def revert_type_scoped_terms
966
+ return self unless has_typed_scopes
967
+
968
+ ctx = self . dup
969
+ ctx . term_definitions . each do |term , definition |
970
+ if definition . type_scoped
971
+ if definition . previous_definition
972
+ ctx . term_definitions [ term ] = definition . previous_definition
973
+ else
974
+ ctx . term_definitions . delete ( term )
975
+ end
976
+ end
977
+ end
978
+
979
+ ctx
980
+ end
981
+
937
982
# Set term mapping
938
983
#
939
984
# @param [#to_s] term
0 commit comments