Method: Hocon::Impl::ConfigNodeObject#add_value_on_path

Defined in:
lib/hocon/impl/config_node_object.rb

#add_value_on_path(desired_path, value, flavor) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/hocon/impl/config_node_object.rb', line 173

def add_value_on_path(desired_path, value, flavor)
  path = desired_path.value
  children_copy = @children.clone
  indentation = indentation().clone

  # If the value we're inserting is a complex value, we'll need to indent it for insertion
  if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) && indentation.length > 0
    indented_value = value.indent_text(indentation[-1])
  else
    indented_value = value
  end
  same_line = !(indentation.length > 0 && indentation[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
                  Tokens.newline?(indentation[0].token))

  # If the path is of length greater than one, see if the value needs to be added further down
  if path.length > 1
     (0..@children.size - 1).reverse_each do |i|
      unless @children[i].is_a?(Hocon::Impl::ConfigNodeField)
        next
      end
      node = @children[i]
      key = node.path.value
      if path.starts_with(key) && node.value.is_a?(self.class)
        remaining_path = desired_path.sub_path(key.length)
        new_value = node.value
        children_copy[i] = node.replace_value(new_value.add_value_on_path(remaining_path, value, flavor))
        return self.class.new(children_copy)
      end
    end
  end

  # Otherwise, construct the new setting
  starts_with_brace = @children[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) && @children[0].token.equal?(Tokens::OPEN_CURLY)
  new_nodes = []
  new_nodes += indentation
  new_nodes.push(desired_path.first)
  new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
  new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COLON))
  new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))

  if path.length == 1
    new_nodes.push(indented_value)
  else
    # If the path is of length greater than one add the required new objects along the path
    new_object_nodes = []
    new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::OPEN_CURLY))
    if indentation.empty?
      new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_line(nil)))
    end
    new_object_nodes += indentation
    new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::CLOSE_CURLY))
    new_object = self.class.new(new_object_nodes)
    new_nodes.push(new_object.add_value_on_path(desired_path.sub_path(1), indented_value, flavor))
  end

  # Combine these two cases so that we only have to iterate once
  if flavor.equal?(ConfigSyntax::JSON) || starts_with_brace || same_line
    i = children_copy.size - 1
    while i >= 0

      # If we are in JSON or are adding a setting on the same line, we need to add a comma to the
      # last setting
      if (flavor.equal?(ConfigSyntax::JSON) || same_line) && children_copy[i].is_a?(Hocon::Impl::ConfigNodeField)
        if i + 1 >= children_copy.size ||
            !(children_copy[i + 1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && children_copy[i + 1].token.equal?(Tokens::COMMA))
          children_copy.insert(i + 1, Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COMMA))
          break
        end
      end

      # Add the value into the copy of the children map, keeping any whitespace/newlines
      # before the close curly brace
      if starts_with_brace && children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
          children_copy[i].token == Tokens::CLOSE_CURLY
        previous = children_copy[i - 1]
        if previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(previous.token)
          children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
          i -= 1
        elsif previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(previous.token)
          before_previous = children_copy[i - 2]
          if same_line
            children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
            i -= 1
          elsif before_previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(before_previous.token)
            children_copy.insert(i - 2, Hocon::Impl::ConfigNodeField.new(new_nodes))
            i -= 2
          else
            children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
          end
        else
          children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
        end
      end

      i -= 1
    end
  end
  unless starts_with_brace
    if children_copy[-1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(children_copy[-1].token)
      children_copy.insert(-2, Hocon::Impl::ConfigNodeField.new(new_nodes))
    else
      children_copy.push(Hocon::Impl::ConfigNodeField.new(new_nodes))
    end
  end
  self.class.new(children_copy)
end