Module: JSON::LD::Flatten

Includes:
Utils
Included in:
API
Defined in:
lib/json/ld/flatten.rb

Instance Method Summary collapse

Methods included from Utils

#blank_node?, #list?, #node?, #node_reference?, #value?

Instance Method Details

#generate_node_map(element, node_map, graph, list, namer, id = nil) ⇒ Object

Build hash of nodes used for framing. Also returns flattened representation of input.

Parameters:

  • element (Array, Hash)

    Expanded element

  • node_map (Hash{String => Hash})

    map of nodes

  • graph (String)

    Graph name for results

  • list (Array)

    List for saving list elements

  • namer (BlankNodeNamer)
  • id (String) (defaults to: nil)

    Identifier already associated with element



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/json/ld/flatten.rb', line 19

def generate_node_map(element, node_map, graph, list, namer, id = nil)
  depth do
    debug("nodeMap") {"element: #{element.inspect}, graph: #{graph}"}
    if element.is_a?(Array)
      element.map {|o| generate_node_map(o, node_map, graph, list, namer)}
    elsif !element.is_a?(Hash) || value?(element)
      list << element if list
    else
      # If the @id property exists and is an IRI, set id to its value, otherwise set it to a blank node identifier created by the Generate Blank Node Identifier algorithm.
      # FIXME: (spec) id passed when it is allocated, so it is not re-allocated here.
      id ||= blank_node?(element) ? namer.get_name(element.fetch('@id', nil)) : element['@id']
      
      # If list is not nil, append a new node reference to list using id at the value for @id.
      list << {'@id' => id} if list

      # Let nodes be the value in nodeMap where the key is graph; if no such value exists, insert a new JSON object for the key graph.
      debug("nodeMap") {"new graph: #{graph}"} unless node_map.has_key?(graph)
      nodes = (node_map[graph] ||= Hash.ordered)

      # If id is not in nodes, create a new JSON object node with id as the value for @id. Let node be the value of id in nodes.
      debug("nodeMap") {"new node: #{id}"} unless nodes.has_key?(id)
      node = (nodes[id] ||= Hash.ordered)
      node['@id'] ||= id

      # For each property that is not @id and each value in element ordered by property:
      element.each do |prop, value|
        case prop
        when '@id'
          # Skip @id, already assigned
        when '@graph'
          # If property is @graph, recursively call this algorithm passing value for element, nodeMap, nil for list and if graph is @merged use graph, otherwise use id for graph and then continue.
          graph = graph == '@merged' ? '@merged' : id
          generate_node_map(value, node_map, graph, nil, namer)
        when /^@(?!type)/
          # If property is not @type and is a keyword, merge property and value into node and then continue.
          debug("nodeMap") {"merge keyword#{prop}: #{value.inspect}"}
          node[prop] = value
        else
          raise InvalidFrame::Syntax,
            "unexpected value: #{value.inspect}, expected array" unless
            value.is_a?(Array)
          
          # For each value v in the array value:
          value.each do |v|
            if node?(v) || node_reference?(v)
              debug("nodeMap") {"node value #{prop}: #{v.inspect}"}
              # If v is a node definition or node reference:
              # If the property @id is not an IRI or it does not exist, map v to a new blank node identifier to avoid collisions.
              name = blank_node?(v) ?
                namer.get_name(v.fetch('@id', nil)) :
                v['@id']

              # If one does not already exist, add a node reference for v into node for property.
              node[prop] ||= []
              node[prop] << {'@id' => name} unless node[prop].any? {|n|
                node_reference?(n) && n['@id'] == name
              }

              # Recursively call this algorithm passing v for value, nodeMap, graph, and nil for list.
              generate_node_map(v, node_map, graph, nil, namer, name)
            elsif list?(v)
              # Otherwise if v has the property @list then recursively call this algorithm with the value of @list as element, nodeMap, graph, and a new array flattenedList as list.
              debug("nodeMap") {"list value #{prop}: #{v.inspect}"}
              flattened_list = []
              generate_node_map(v['@list'],
                node_map,
                graph,
                flattened_list,
                namer)
              # Create a new JSON object with the property @list set to flattenedList and add it to node for property.
              (node[prop] ||= []) << {'@list' => flattened_list}
            elsif prop == '@type'
              # Otherwise, if property is @type and v is not an IRI, generate a new blank node identifier and add it to node for property.
              # FIXME: @type shouldn't really be a BNode, and not clear how we even get here from expansion
              name = blank_node?({'@id' => v}) ? namer.get_name(v) : v
              (node[prop] ||= []) << name
            else
              debug("nodeMap") {"value #{prop}: #{v.inspect}"}
              # Otherwise, add v to node for property.
              (node[prop] ||= []) << v
            end
          end
        end
      end
    end

    debug("nodeMap") {node_map.to_json(JSON_STATE)}
  end
end