hocon 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +4 -2
  3. data/lib/hocon.rb +2 -0
  4. data/lib/hocon/config.rb +1010 -0
  5. data/lib/hocon/config_error.rb +32 -2
  6. data/lib/hocon/config_factory.rb +46 -0
  7. data/lib/hocon/config_include_context.rb +49 -0
  8. data/lib/hocon/config_includer_file.rb +27 -0
  9. data/lib/hocon/config_list.rb +49 -0
  10. data/lib/hocon/config_mergeable.rb +74 -0
  11. data/lib/hocon/config_object.rb +144 -1
  12. data/lib/hocon/config_parse_options.rb +33 -9
  13. data/lib/hocon/config_parseable.rb +51 -0
  14. data/lib/hocon/config_render_options.rb +4 -2
  15. data/lib/hocon/config_resolve_options.rb +31 -0
  16. data/lib/hocon/config_syntax.rb +5 -2
  17. data/lib/hocon/config_util.rb +73 -0
  18. data/lib/hocon/config_value.rb +122 -0
  19. data/lib/hocon/config_value_factory.rb +66 -2
  20. data/lib/hocon/config_value_type.rb +5 -2
  21. data/lib/hocon/impl.rb +2 -0
  22. data/lib/hocon/impl/abstract_config_node.rb +29 -0
  23. data/lib/hocon/impl/abstract_config_node_value.rb +11 -0
  24. data/lib/hocon/impl/abstract_config_object.rb +148 -42
  25. data/lib/hocon/impl/abstract_config_value.rb +251 -11
  26. data/lib/hocon/impl/array_iterator.rb +19 -0
  27. data/lib/hocon/impl/config_boolean.rb +7 -1
  28. data/lib/hocon/impl/config_concatenation.rb +177 -28
  29. data/lib/hocon/impl/config_delayed_merge.rb +329 -0
  30. data/lib/hocon/impl/config_delayed_merge_object.rb +274 -0
  31. data/lib/hocon/impl/config_document_parser.rb +647 -0
  32. data/lib/hocon/impl/config_double.rb +44 -0
  33. data/lib/hocon/impl/config_impl.rb +143 -19
  34. data/lib/hocon/impl/config_impl_util.rb +18 -0
  35. data/lib/hocon/impl/config_include_kind.rb +10 -0
  36. data/lib/hocon/impl/config_int.rb +13 -1
  37. data/lib/hocon/impl/config_node_array.rb +11 -0
  38. data/lib/hocon/impl/config_node_comment.rb +19 -0
  39. data/lib/hocon/impl/config_node_complex_value.rb +54 -0
  40. data/lib/hocon/impl/config_node_concatenation.rb +11 -0
  41. data/lib/hocon/impl/config_node_field.rb +81 -0
  42. data/lib/hocon/impl/config_node_include.rb +33 -0
  43. data/lib/hocon/impl/config_node_object.rb +276 -0
  44. data/lib/hocon/impl/config_node_path.rb +48 -0
  45. data/lib/hocon/impl/config_node_root.rb +60 -0
  46. data/lib/hocon/impl/config_node_simple_value.rb +42 -0
  47. data/lib/hocon/impl/config_node_single_token.rb +17 -0
  48. data/lib/hocon/impl/config_null.rb +15 -7
  49. data/lib/hocon/impl/config_number.rb +43 -4
  50. data/lib/hocon/impl/config_parser.rb +403 -0
  51. data/lib/hocon/impl/config_reference.rb +142 -0
  52. data/lib/hocon/impl/config_string.rb +55 -7
  53. data/lib/hocon/impl/container.rb +29 -0
  54. data/lib/hocon/impl/default_transformer.rb +24 -15
  55. data/lib/hocon/impl/from_map_mode.rb +3 -1
  56. data/lib/hocon/impl/full_includer.rb +2 -0
  57. data/lib/hocon/impl/memo_key.rb +42 -0
  58. data/lib/hocon/impl/mergeable_value.rb +8 -0
  59. data/lib/hocon/impl/origin_type.rb +8 -2
  60. data/lib/hocon/impl/parseable.rb +455 -91
  61. data/lib/hocon/impl/path.rb +181 -59
  62. data/lib/hocon/impl/path_builder.rb +24 -3
  63. data/lib/hocon/impl/path_parser.rb +280 -0
  64. data/lib/hocon/impl/replaceable_merge_stack.rb +22 -0
  65. data/lib/hocon/impl/resolve_context.rb +254 -0
  66. data/lib/hocon/impl/resolve_memos.rb +21 -0
  67. data/lib/hocon/impl/resolve_result.rb +39 -0
  68. data/lib/hocon/impl/resolve_source.rb +354 -0
  69. data/lib/hocon/impl/resolve_status.rb +3 -1
  70. data/lib/hocon/impl/simple_config.rb +264 -10
  71. data/lib/hocon/impl/simple_config_document.rb +48 -0
  72. data/lib/hocon/impl/simple_config_list.rb +282 -8
  73. data/lib/hocon/impl/simple_config_object.rb +424 -88
  74. data/lib/hocon/impl/simple_config_origin.rb +263 -71
  75. data/lib/hocon/impl/simple_include_context.rb +31 -1
  76. data/lib/hocon/impl/simple_includer.rb +196 -1
  77. data/lib/hocon/impl/substitution_expression.rb +38 -0
  78. data/lib/hocon/impl/token.rb +17 -4
  79. data/lib/hocon/impl/token_type.rb +6 -2
  80. data/lib/hocon/impl/tokenizer.rb +339 -109
  81. data/lib/hocon/impl/tokens.rb +330 -79
  82. data/lib/hocon/impl/unmergeable.rb +14 -1
  83. data/lib/hocon/impl/unsupported_operation_error.rb +6 -0
  84. data/lib/hocon/impl/url.rb +37 -0
  85. data/lib/hocon/parser.rb +7 -0
  86. data/lib/hocon/parser/config_document.rb +92 -0
  87. data/lib/hocon/parser/config_document_factory.rb +36 -0
  88. data/lib/hocon/parser/config_node.rb +30 -0
  89. metadata +67 -43
  90. data/lib/hocon/impl/config_float.rb +0 -13
  91. data/lib/hocon/impl/parser.rb +0 -977
  92. data/lib/hocon/impl/properties_parser.rb +0 -83
@@ -1,28 +1,34 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon/impl'
2
4
  require 'hocon/impl/simple_config_origin'
3
5
  require 'hocon/impl/abstract_config_object'
4
6
  require 'hocon/impl/resolve_status'
7
+ require 'hocon/impl/resolve_result'
8
+ require 'hocon/impl/path'
5
9
  require 'hocon/config_error'
6
10
  require 'set'
11
+ require 'forwardable'
12
+
7
13
 
8
- class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
14
+ class Hocon::Impl::SimpleConfigObject
15
+ include Hocon::Impl::AbstractConfigObject
16
+ extend Forwardable
9
17
 
10
18
  ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
11
19
  ResolveStatus = Hocon::Impl::ResolveStatus
20
+ ResolveResult = Hocon::Impl::ResolveResult
12
21
  SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
22
+ Path = Hocon::Impl::Path
13
23
 
14
- def self.empty_missing(base_origin)
15
- self.new(
16
- Hocon::Impl::SimpleConfigOrigin.new_simple("#{base_origin.description} (not found)"),
17
- {})
18
- end
19
24
 
20
- def initialize(origin, value,
21
- status = Hocon::Impl::ResolveStatus.from_values(value.values),
22
- ignores_fallbacks = false)
25
+ def initialize(origin,
26
+ value,
27
+ status = Hocon::Impl::ResolveStatus.from_values(value.values),
28
+ ignores_fallbacks = false)
23
29
  super(origin)
24
30
  if value.nil?
25
- raise ConfigBugError, "creating config object with null map"
31
+ raise ConfigBugOrBrokenError, "creating config object with null map"
26
32
  end
27
33
  @value = value
28
34
  @resolved = (status == Hocon::Impl::ResolveStatus::RESOLVED)
@@ -30,15 +36,170 @@ class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
30
36
 
31
37
  # Kind of an expensive debug check. Comment out?
32
38
  if status != Hocon::Impl::ResolveStatus.from_values(value.values)
33
- raise ConfigBugError, "Wrong resolved status on #{self}"
39
+ raise ConfigBugOrBrokenError, "Wrong resolved status on #{self}"
34
40
  end
35
41
  end
36
42
 
37
43
  attr_reader :value
44
+ # To support accessing ConfigObjects like a hash
45
+ def_delegators :@value, :[], :has_key?, :has_value?, :empty?, :size, :keys, :values, :each, :map
46
+
47
+
48
+ def with_only_key(key)
49
+ with_only_path(Path.new_key(key))
50
+ end
51
+
52
+ def without_key(key)
53
+ without_path(Path.new_key(key))
54
+ end
55
+
56
+ # gets the object with only the path if the path
57
+ # exists, otherwise null if it doesn't. this ensures
58
+ # that if we have { a : { b : 42 } } and do
59
+ # withOnlyPath("a.b.c") that we don't keep an empty
60
+ # "a" object.
61
+ def with_only_path_or_nil(path)
62
+ key = path.first
63
+ path_next = path.remainder
64
+ v = value[key]
65
+
66
+ if ! path_next.nil?
67
+ if (!v.nil?) && (v.is_a?(Hocon::Impl::AbstractConfigObject))
68
+ v = v.with_only_path_or_nil(path_next)
69
+ else
70
+ # if the path has more elements but we don't have an object,
71
+ # then the rest of the path does not exist.
72
+ v = nil
73
+ end
74
+ end
75
+
76
+ if v.nil?
77
+ nil
78
+ else
79
+ self.class.new(origin, {key => v}, v.resolve_status, @ignores_fallbacks)
80
+ end
81
+ end
82
+
83
+ def with_only_path(path)
84
+ o = with_only_path_or_nil(path)
85
+ if o.nil?
86
+ self.class.new(origin, {}, ResolveStatus::RESOLVED, @ignores_fallbacks)
87
+ else
88
+ o
89
+ end
90
+ end
91
+
92
+ def without_path(path)
93
+ key = path.first
94
+ remainder = path.remainder
95
+ v = @value[key]
96
+
97
+ if (not v.nil?) && (not remainder.nil?) && v.is_a?(Hocon::Impl::AbstractConfigObject)
98
+ v = v.without_path(remainder)
99
+ updated = @value.clone
100
+ updated[key] = v
101
+ self.class.new(origin,
102
+ updated,
103
+ ResolveStatus.from_values(updated.values), @ignores_fallbacks)
104
+ elsif (not remainder.nil?) || v.nil?
105
+ return self
106
+ else
107
+ smaller = Hash.new
108
+ @value.each do |old_key, old_value|
109
+ unless old_key == key
110
+ smaller[old_key] = old_value
111
+ end
112
+ end
113
+ self.class.new(origin,
114
+ smaller,
115
+ ResolveStatus.from_values(smaller.values), @ignores_fallbacks)
116
+ end
117
+ end
118
+
119
+ def with_value(path, v)
120
+ key = path.first
121
+ remainder = path.remainder
122
+
123
+ if remainder.nil?
124
+ with_key_value(key, v)
125
+ else
126
+ child = @value[key]
127
+ if (not child.nil?) && child.is_a?(Hocon::Impl::AbstractConfigObject)
128
+ return with_key_value(key, child.with_value(remainder, v))
129
+ else
130
+ subtree = v.at_path_with_origin(
131
+ SimpleConfigOrigin.new_simple("with_value(#{remainder.render})"), remainder)
132
+ with_key_value(key, subtree.root)
133
+ end
134
+ end
135
+ end
136
+
137
+ def with_key_value(key, v)
138
+ if v.nil?
139
+ raise ConfigBugOrBrokenError.new("Trying to store null ConfigValue in a ConfigObject")
140
+ end
141
+
142
+ new_map = Hash.new
143
+ if @value.empty?
144
+ new_map[key] = v
145
+ else
146
+ new_map = @value.clone
147
+ new_map[key] = v
148
+ end
149
+ self.class.new(origin, new_map, ResolveStatus.from_values(new_map.values), @ignores_fallbacks)
150
+ end
151
+
152
+ def attempt_peek_with_partial_resolve(key)
153
+ @value[key]
154
+ end
38
155
 
39
156
  def new_copy_with_status(new_status, new_origin, new_ignores_fallbacks = nil)
40
- Hocon::Impl::SimpleConfigObject.new(new_origin,
41
- @value, new_status, new_ignores_fallbacks)
157
+ self.class.new(new_origin, @value, new_status, new_ignores_fallbacks)
158
+ end
159
+
160
+ def with_fallbacks_ignored()
161
+ if @ignores_fallbacks
162
+ self
163
+ else
164
+ new_copy_with_status(resolve_status, origin, true)
165
+ end
166
+ end
167
+
168
+ def resolve_status
169
+ ResolveStatus.from_boolean(@resolved)
170
+ end
171
+
172
+ def replace_child(child, replacement)
173
+ new_children = @value.clone
174
+ new_children.each do |old, old_value|
175
+ if old_value.equal?(child)
176
+ if replacement != nil
177
+ new_children[old] = replacement
178
+ else
179
+ new_children.delete(old)
180
+ end
181
+
182
+ return self.class.new(origin, new_children, ResolveStatus.from_values(new_children.values),
183
+ @ignores_fallbacks)
184
+ end
185
+ end
186
+ raise ConfigBugOrBrokenError, "SimpleConfigObject.replaceChild did not find #{child} in #{self}"
187
+ end
188
+
189
+ def has_descendant?(descendant)
190
+ value.values.each do |child|
191
+ if child.equal?(descendant)
192
+ return true
193
+ end
194
+ end
195
+ # now do the expensive search
196
+ value.values.each do |child|
197
+ if child.is_a?(Hocon::Impl::Container) && child.has_descendant?(descendant)
198
+ return true
199
+ end
200
+ end
201
+
202
+ false
42
203
  end
43
204
 
44
205
  def ignores_fallbacks?
@@ -46,14 +207,18 @@ class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
46
207
  end
47
208
 
48
209
  def unwrapped
49
- @value.merge(@value) { |k,v| v.unwrapped }
210
+ m = {}
211
+ @value.each do |k,v|
212
+ m[k] = v.unwrapped
213
+ end
214
+ m
50
215
  end
51
216
 
52
217
  def merged_with_object(abstract_fallback)
53
218
  require_not_ignoring_fallbacks
54
219
 
55
220
  unless abstract_fallback.is_a?(Hocon::Impl::SimpleConfigObject)
56
- raise ConfigBugError, "should not be reached (merging non-SimpleConfigObject)"
221
+ raise ConfigBugOrBrokenError, "should not be reached (merging non-SimpleConfigObject)"
57
222
  end
58
223
 
59
224
  fallback = abstract_fallback
@@ -87,46 +252,204 @@ class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
87
252
  new_ignores_fallbacks = fallback.ignores_fallbacks?
88
253
 
89
254
  if changed
90
- Hocon::Impl::SimpleConfigObject.new(merge_origins([self, fallback]),
91
- merged, new_resolve_status,
92
- new_ignores_fallbacks)
255
+ Hocon::Impl::SimpleConfigObject.new(Hocon::Impl::AbstractConfigObject.merge_origins([self, fallback]),
256
+ merged, new_resolve_status,
257
+ new_ignores_fallbacks)
93
258
  elsif (new_resolve_status != resolve_status) || (new_ignores_fallbacks != ignores_fallbacks?)
94
- newCopy(new_resolve_status, origin, new_ignores_fallbacks)
259
+ new_copy_with_status(new_resolve_status, origin, new_ignores_fallbacks)
95
260
  else
96
261
  self
97
262
  end
98
263
  end
99
264
 
100
- def render_value_to_sb(sb, indent_size, at_root, options)
265
+ def modify(modifier)
266
+ begin
267
+ modify_may_throw(modifier)
268
+ rescue Hocon::ConfigError => e
269
+ raise e
270
+ end
271
+ end
272
+
273
+ def modify_may_throw(modifier)
274
+ changes = nil
275
+ keys.each do |k|
276
+ v = value[k]
277
+ # "modified" may be null, which means remove the child;
278
+ # to do that we put null in the "changes" map.
279
+ modified = modifier.modify_child_may_throw(k, v)
280
+ if ! modified.equal?(v)
281
+ if changes.nil?
282
+ changes = {}
283
+ end
284
+ changes[k] = modified
285
+ end
286
+ end
287
+ if changes.nil?
288
+ self
289
+ else
290
+ modified = {}
291
+ saw_unresolved = false
292
+ keys.each do |k|
293
+ if changes.has_key?(k)
294
+ new_value = changes[k]
295
+ if ! new_value.nil?
296
+ modified[k] = new_value
297
+ if new_value.resolve_status == ResolveStatus::UNRESOLVED
298
+ saw_unresolved = true
299
+ end
300
+ else
301
+ # remove this child; don't put it in the new map
302
+ end
303
+ else
304
+ new_value = value[k]
305
+ modified[k] = new_value
306
+ if new_value.resolve_status == ResolveStatus::UNRESOLVED
307
+ saw_unresolved = true
308
+ end
309
+ end
310
+ end
311
+ self.class.new(origin, modified,
312
+ saw_unresolved ? ResolveStatus::UNRESOLVED : ResolveStatus::RESOLVED,
313
+ @ignores_fallbacks)
314
+ end
315
+ end
316
+
317
+
318
+ class ResolveModifier
319
+
320
+ attr_accessor :context
321
+ attr_reader :source
322
+
323
+ def initialize(context, source)
324
+ @context = context
325
+ @source = source
326
+ @original_restrict = context.restrict_to_child
327
+ end
328
+
329
+ def modify_child_may_throw(key, v)
330
+ if @context.is_restricted_to_child
331
+ if key == @context.restrict_to_child.first
332
+ remainder = @context.restrict_to_child.remainder
333
+ if remainder != nil
334
+ result = @context.restrict(remainder).resolve(v, @source)
335
+ @context = result.context.unrestricted.restrict(@original_restrict)
336
+ return result.value
337
+ else
338
+ # we don't want to resolve the leaf child
339
+ return v
340
+ end
341
+ else
342
+ # not in the restrictToChild path
343
+ return v
344
+ end
345
+ else
346
+ # no restrictToChild, resolve everything
347
+ result = @context.unrestricted.resolve(v, @source)
348
+ @context = result.context.unrestricted.restrict(@original_restrict)
349
+ result.value
350
+ end
351
+ end
352
+ end
353
+
354
+ def resolve_substitutions(context, source)
355
+ if resolve_status == ResolveStatus::RESOLVED
356
+ return ResolveResult.make(context, self)
357
+ end
358
+
359
+ source_with_parent = source.push_parent(self)
360
+
361
+ begin
362
+ modifier = ResolveModifier.new(context, source_with_parent)
363
+
364
+ value = modify_may_throw(modifier)
365
+ ResolveResult.make(modifier.context, value)
366
+
367
+ rescue NotPossibleToResolve => e
368
+ raise e
369
+ rescue Hocon::ConfigError => e
370
+ raise e
371
+ end
372
+ end
373
+
374
+ def relativized(prefix)
375
+
376
+ modifier = Class.new do
377
+ include Hocon::Impl::AbstractConfigValue::NoExceptionsModifier
378
+
379
+ # prefix isn't in scope inside of a def, but it is in scope inside of Class.new
380
+ # so manually define a method that has access to prefix
381
+ # I feel dirty
382
+ define_method(:modify_child) do |key, v|
383
+ v.relativized(prefix)
384
+ end
385
+ end
386
+
387
+ modify(modifier.new)
388
+ end
389
+
390
+ class RenderComparator
391
+ def self.all_digits?(s)
392
+ s =~ /^\d+$/
393
+ end
394
+
395
+ # This is supposed to sort numbers before strings,
396
+ # and sort the numbers numerically. The point is
397
+ # to make objects which are really list-like
398
+ # (numeric indices) appear in order.
399
+ def self.sort(arr)
400
+ arr.sort do |a, b|
401
+ a_digits = all_digits?(a)
402
+ b_digits = all_digits?(b)
403
+ if a_digits && b_digits
404
+ Integer(a) <=> Integer(b)
405
+ elsif a_digits
406
+ -1
407
+ elsif b_digits
408
+ 1
409
+ else
410
+ a <=> b
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ def render_value_to_sb(sb, indent, at_root, options)
101
417
  if empty?
102
418
  sb << "{}"
103
419
  else
104
420
  outer_braces = options.json? || !at_root
105
421
 
106
- inner_indent =
107
- if outer_braces
108
- sb << "{"
109
- if options.formatted?
110
- sb << "\n"
111
- end
112
- indent_size + 1
113
- else
114
- indent_size
422
+ if outer_braces
423
+ inner_indent = indent + 1
424
+ sb << "{"
425
+
426
+ if options.formatted?
427
+ sb << "\n"
115
428
  end
429
+ else
430
+ inner_indent = indent
431
+ end
116
432
 
117
433
  separator_count = 0
118
- key_set.each do |k|
434
+ sorted_keys = RenderComparator.sort(keys)
435
+ sorted_keys.each do |k|
119
436
  v = @value[k]
120
437
 
121
438
  if options.origin_comments?
122
- indent(sb, inner_indent, options)
123
- sb << "# "
124
- sb << v.origin.description
125
- sb << "\n"
439
+ lines = v.origin.description.split("\n")
440
+ lines.each { |l|
441
+ Hocon::Impl::AbstractConfigValue.indent(sb, indent + 1, options)
442
+ sb << '#'
443
+ unless l.empty?
444
+ sb << ' '
445
+ end
446
+ sb << l
447
+ sb << "\n"
448
+ }
126
449
  end
127
450
  if options.comments?
128
451
  v.origin.comments.each do |comment|
129
- indent(sb, inner_indent, options)
452
+ Hocon::Impl::AbstractConfigValue.indent(sb, inner_indent, options)
130
453
  sb << "#"
131
454
  if !comment.start_with?(" ")
132
455
  sb << " "
@@ -135,7 +458,7 @@ class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
135
458
  sb << "\n"
136
459
  end
137
460
  end
138
- indent(sb, inner_indent, options)
461
+ Hocon::Impl::AbstractConfigValue.indent(sb, inner_indent, options)
139
462
  v.render_to_sb(sb, inner_indent, false, k.to_s, options)
140
463
 
141
464
  if options.formatted?
@@ -161,7 +484,7 @@ class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
161
484
  if options.formatted?
162
485
  sb << "\n" # put a newline back
163
486
  if outer_braces
164
- indent(sb, indent_size, options)
487
+ Hocon::Impl::AbstractConfigValue.indent(sb, indent, options)
165
488
  end
166
489
  end
167
490
  sb << "}"
@@ -172,74 +495,87 @@ class Hocon::Impl::SimpleConfigObject < Hocon::Impl::AbstractConfigObject
172
495
  end
173
496
  end
174
497
 
498
+ def self.map_equals(a, b)
499
+ if a.equal?(b)
500
+ return true
501
+ end
175
502
 
176
- def key_set
177
- Set.new(@value.keys)
178
- end
503
+ # Hashes aren't ordered in ruby, so sort first
504
+ if not a.keys.sort == b.keys.sort
505
+ return false
506
+ end
507
+
508
+ a.keys.each do |key|
509
+ if a[key] != b[key]
510
+ return false
511
+ end
512
+ end
179
513
 
180
- def empty?
181
- @value.empty?
514
+ true
182
515
  end
183
516
 
184
- def attempt_peek_with_partial_resolve(key)
517
+ def get(key)
185
518
  @value[key]
186
519
  end
187
520
 
188
- def without_path(path)
189
- key = path.first
190
- remainder = path.remainder
191
- v = @value[key]
521
+ def self.map_hash(m)
522
+ # the keys have to be sorted, otherwise we could be equal
523
+ # to another map but have a different hashcode.
524
+ keys = m.keys.sort
192
525
 
193
- if (not v.nil?) && (not remainder.nil?) && v.is_a?(Hocon::Impl::AbstractConfigObject)
194
- v = v.without_path(remainder)
195
- updated = @value.clone
196
- updated[key] = v
197
- return Hocon::Impl::SimpleConfigObject.new(origin, updated,
198
- ResolveStatus.from_values(updated.values), @ignores_fallbacks)
199
- elsif (not remainder.nil?) || v.nil?
200
- return self
201
- else
202
- smaller = Hash.new
203
- @value.each do |old_key, old_value|
204
- if not old_key == key
205
- smaller[old_key] = old_value
206
- end
207
- end
208
- return Hocon::Impl::SimpleConfigObject.new(origin, smaller,
209
- ResolveStatus.from_values(smaller.values), @ignores_fallbacks)
526
+ value_hash = 0
527
+
528
+ keys.each do |key|
529
+ value_hash += m[key].hash
210
530
  end
531
+
532
+ 41 * (41 + keys.hash) + value_hash
211
533
  end
212
534
 
213
- def with_value(path, v)
214
- key = path.first
215
- remainder = path.remainder
535
+ def can_equal(other)
536
+ other.is_a? Hocon::ConfigObject
537
+ end
216
538
 
217
- if remainder.nil?
218
- return with_value_impl(key, v)
539
+ def ==(other)
540
+ # note that "origin" is deliberately NOT part of equality.
541
+ # neither are other "extras" like ignoresFallbacks or resolve status.
542
+ if other.is_a? Hocon::ConfigObject
543
+ # optimization to avoid unwrapped() for two ConfigObject,
544
+ # which is what AbstractConfigValue does.
545
+ can_equal(other) && self.class.map_equals(self, other)
219
546
  else
220
- child = @value[key]
221
- if (not child.nil?) && child.is_a?(Hocon::Impl::AbstractConfigObject)
222
- return with_value_impl(key, child.with_value(remainder, v))
223
- else
224
- subtree = v.at_path(
225
- SimpleConfigOrigin.new_simple("with_value(#{remainder.render})"), remainder)
226
- with_value_impl(key, subtree.root)
227
- end
547
+ false
228
548
  end
229
549
  end
230
550
 
231
- def with_value_impl(key, v)
232
- if v.nil?
233
- raise ConfigBugOrBrokenError.new("Trying to store null ConfigValue in a ConfigObject", nil)
234
- end
551
+ def hash
552
+ self.class.map_hash(@value)
553
+ end
235
554
 
236
- new_map = Hash.new
237
- if @value.empty?
238
- new_map[key] = v
555
+ def contains_key?(key)
556
+ @value.has_key?(key)
557
+ end
558
+
559
+ def key_set
560
+ Set.new(@value.keys)
561
+ end
562
+
563
+ def contains_value?(v)
564
+ @value.has_value?(v)
565
+ end
566
+
567
+ def self.empty(origin = nil)
568
+ if origin.nil?
569
+ empty(Hocon::Impl::SimpleConfigOrigin.new_simple("empty config"))
239
570
  else
240
- new_map = @value.clone
241
- new_map[key] = v
571
+ self.new(origin, {})
242
572
  end
243
- self.class.new(origin, new_map, ResolveStatus.from_values(new_map.values), @ignores_fallbacks)
244
573
  end
245
- end
574
+
575
+ def self.empty_missing(base_origin)
576
+ self.new(
577
+ Hocon::Impl::SimpleConfigOrigin.new_simple("#{base_origin.description} (not found)"),
578
+ {})
579
+ end
580
+ end
581
+