From 61d1bddb163fdd8813bd2377c8ed392c515fc3dc Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Tue, 11 Dec 2012 18:02:46 -0800 Subject: [PATCH] Annotation expansion. --- lib/json/ld/evaluation_context.rb | 2 +- lib/json/ld/expand.rb | 31 +++++++++++++++++++++++---- spec/expand_spec.rb | 35 +++++++++++++++++++++++++++++++ spec/suite_expand_spec.rb | 2 -- 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/lib/json/ld/evaluation_context.rb b/lib/json/ld/evaluation_context.rb index c0db0b72..009c582c 100644 --- a/lib/json/ld/evaluation_context.rb +++ b/lib/json/ld/evaluation_context.rb @@ -237,7 +237,7 @@ def parse(context) new_ec.set_coerce(key, iri) end when '@container' - raise InvalidContext::Syntax, "unknown mapping for '@container' to #{value2.inspect}" unless %w(@list @set @language).include?(value2) + raise InvalidContext::Syntax, "unknown mapping for '@container' to #{value2.inspect}" unless %w(@list @set @language @annotation).include?(value2) if new_ec.container(key) != value2 debug("parse") {"container #{key.inspect} as #{value2.inspect}"} new_ec.set_container(key, value2) diff --git a/lib/json/ld/expand.rb b/lib/json/ld/expand.rb index 97c78993..0d11ca4a 100644 --- a/lib/json/ld/expand.rb +++ b/lib/json/ld/expand.rb @@ -94,6 +94,11 @@ def expand(input, active_property, context, options = {}) else context.expand_iri(value, options.merge(:position => :property, :quiet => true)).to_s end + when '@annotation' + # Otherwise, if the property is @annotation, the value MUST be a string + value = value.first if value.is_a?(Array) && value.length == 1 + raise ProcessingError, "Value of @annotation is not a string: #{value.inspect}" unless value.is_a?(String) + value when '@value', '@language' # Otherwise, if the property is @value or @language the value must not be a JSON object or an array. raise ProcessingError::Lossy, "Value of #{property} must be a string, was #{value.inspect}" if value.is_a?(Hash) || value.is_a?(Array) @@ -113,7 +118,7 @@ def expand(input, active_property, context, options = {}) value else - if context.container(active_property) == '@language' + if context.container(active_property) == '@language' && value.is_a?(Hash) # Otherwise, if value is a JSON object and property is not a keyword and its associated term entry in the active context has a @container key associated with a value of @language, process the associated value as a language map: # Set multilingual array to an empty array. @@ -138,6 +143,24 @@ def expand(input, active_property, context, options = {}) end # Set the value associated with property to the multilingual array. multilingual_array + elsif context.container(active_property) == '@annotation' && value.is_a?(Hash) + # Otherwise, if value is a JSON object and property is not a keyword and its associated term entry in the active context has a @container key associated with a value of @annotation, process the associated value as a annotation: + + # Set ary to an empty array. + ary = [] + + # For each key-value in the object: + value.keys.sort.each do |k| + [value[k]].flatten.each do |v| + # Expand the value, adding an '@annotation' key with value equal to the key + expanded_value = depth { expand(v, active_property, context, options) } + next unless expanded_value + expanded_value['@annotation'] ||= k + ary << expanded_value + end + end + # Set the value associated with property to the multilingual array. + ary else # Otherwise, expand value recursively using this algorithm, passing copies of the active context and active property. depth { expand(value, active_property, context, options) } @@ -163,7 +186,7 @@ def expand(input, active_property, context, options = {}) end # Convert value to array form unless value is null or property is @id, @type, @value, or @language. - if !%(@id @language @type @value).include?(property) && !expanded_value.is_a?(Array) + if !%(@id @language @type @value @annotation).include?(property) && !expanded_value.is_a?(Array) debug(" => make #{expanded_value.inspect} an array") expanded_value = [expanded_value] end @@ -184,8 +207,8 @@ def expand(input, active_property, context, options = {}) if output_object.has_key?('@value') output_object.delete('@language') if output_object['@language'].to_s.empty? output_object.delete('@type') if output_object['@type'].to_s.empty? - if output_object.keys.length > 2 || (%w(@language @type) - output_object.keys).empty? - raise ProcessingError, "element must not have more than one other property, which can either be @language or @type with a string value." unless value.is_a?(String) + if (%w(@annotation @language @type) - output_object.keys).empty? + raise ProcessingError, "element must not have more than one other property other than @annotation, which can either be @language or @type with a string value." unless value.is_a?(String) end # if the value of @value equals null, replace element with the value of null. diff --git a/spec/expand_spec.rb b/spec/expand_spec.rb index c85cf6ec..1f458d90 100644 --- a/spec/expand_spec.rb +++ b/spec/expand_spec.rb @@ -728,6 +728,41 @@ end end + context "annotations" do + { + "string annotation" => { + :input => { + "@context" => { + "container" => { + "@id" => "http://example.com/container", + "@container" => "@annotation" + } + }, + "@id" => "http://example.com/annotationsTest", + "container" => { + "en" => "The Queen", + "de" => [ "Die Königin", "Ihre Majestät" ] + } + }, + :output => [ + { + "@id" => "http://example.com/annotationsTest", + "http://example.com/container" => [ + {"@value" => "Die Königin", "@annotation" => "de"}, + {"@value" => "Ihre Majestät", "@annotation" => "de"}, + {"@value" => "The Queen", "@annotation" => "en"} + ] + } + ] + }, + }.each do |title, params| + it title do + jld = JSON::LD::API.expand(params[:input], nil, nil, :debug => @debug) + jld.should produce(params[:output], @debug) + end + end + end + context "exceptions" do { "@list containing @list" => { diff --git a/spec/suite_expand_spec.rb b/spec/suite_expand_spec.rb index b6355e0f..ed17971d 100644 --- a/spec/suite_expand_spec.rb +++ b/spec/suite_expand_spec.rb @@ -11,8 +11,6 @@ specify "#{t.property('input')}: #{t.name}" do begin case t.property('input') - when /expand-(0036|0040)/ - pending("implementation of @annotation") when /expand-(0037|0038|0039)/ pending("implementation of property generators") end