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
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+
5
+ class Hocon::Impl::ArrayIterator
6
+ def initialize(a)
7
+ @a = a
8
+ @index = 0
9
+ end
10
+
11
+ def has_next?
12
+ @index < @a.length
13
+ end
14
+
15
+ def next
16
+ @index += 1
17
+ @a[@index - 1]
18
+ end
19
+ end
@@ -1,12 +1,18 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon/impl'
2
4
  require 'hocon/impl/abstract_config_value'
3
5
 
4
- class Hocon::Impl::ConfigBoolean < Hocon::Impl::AbstractConfigValue
6
+ class Hocon::Impl::ConfigBoolean
7
+ include Hocon::Impl::AbstractConfigValue
8
+
5
9
  def initialize(origin, value)
6
10
  super(origin)
7
11
  @value = value
8
12
  end
9
13
 
14
+ attr_reader :value
15
+
10
16
  def value_type
11
17
  Hocon::ConfigValueType::BOOLEAN
12
18
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon/impl'
2
4
  require 'hocon/impl/abstract_config_value'
3
5
  require 'hocon/impl/abstract_config_object'
@@ -6,14 +8,70 @@ require 'hocon/config_object'
6
8
  require 'hocon/impl/unmergeable'
7
9
  require 'hocon/impl/simple_config_origin'
8
10
  require 'hocon/impl/config_string'
11
+ require 'hocon/impl/container'
9
12
 
10
- class Hocon::Impl::ConfigConcatenation < Hocon::Impl::AbstractConfigValue
13
+ class Hocon::Impl::ConfigConcatenation
11
14
  include Hocon::Impl::Unmergeable
15
+ include Hocon::Impl::Container
16
+ include Hocon::Impl::AbstractConfigValue
12
17
 
13
18
  SimpleConfigList = Hocon::Impl::SimpleConfigList
14
19
  ConfigObject = Hocon::ConfigObject
20
+ ConfigString = Hocon::Impl::ConfigString
21
+ ResolveStatus = Hocon::Impl::ResolveStatus
15
22
  Unmergeable = Hocon::Impl::Unmergeable
16
23
  SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
24
+ ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
25
+ ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
26
+ ConfigWrongTypeError = Hocon::ConfigError::ConfigWrongTypeError
27
+
28
+ attr_reader :pieces
29
+
30
+ def initialize(origin, pieces)
31
+ super(origin)
32
+ @pieces = pieces
33
+
34
+ if pieces.size < 2
35
+ raise ConfigBugOrBrokenError, "Created concatenation with less than 2 items: #{self}"
36
+ end
37
+
38
+ had_unmergeable = false
39
+ pieces.each do |p|
40
+ if p.is_a?(Hocon::Impl::ConfigConcatenation)
41
+ raise ConfigBugOrBrokenError, "ConfigConcatenation should never be nested: #{self}"
42
+ end
43
+ if p.is_a?(Unmergeable)
44
+ had_unmergeable = true
45
+ end
46
+ end
47
+
48
+ unless had_unmergeable
49
+ raise ConfigBugOrBrokenError, "Created concatenation without an unmergeable in it: #{self}"
50
+ end
51
+ end
52
+
53
+ def value_type
54
+ raise not_resolved
55
+ end
56
+
57
+ def unwrapped
58
+ raise not_resolved
59
+ end
60
+
61
+ def new_copy(new_origin)
62
+ self.class.new(new_origin, @pieces)
63
+ end
64
+
65
+ def ignores_fallbacks?
66
+ # we can never ignore fallbacks because if a child ConfigReference
67
+ # is self-referential we have to look lower in the merge stack
68
+ # for its value.
69
+ false
70
+ end
71
+
72
+ def unmerged_values
73
+ [self]
74
+ end
17
75
 
18
76
  #
19
77
  # Add left and right, or their merger, to builder
@@ -25,9 +83,9 @@ class Hocon::Impl::ConfigConcatenation < Hocon::Impl::AbstractConfigValue
25
83
  # check for an object which can be converted to a list
26
84
  # (this will be an object with numeric keys, like foo.0, foo.1)
27
85
  if (left.is_a?(ConfigObject)) && (right.is_a?(SimpleConfigList))
28
- left = DefaultTransformer.transform(left, ConfigValueType::LIST)
86
+ left = Hocon::Impl::DefaultTransformer.transform(left, Hocon::ConfigValueType::LIST)
29
87
  elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(ConfigObject))
30
- right = DefaultTransformer.transform(right, ConfigValueType::LIST)
88
+ right = Hocon::Impl::DefaultTransformer.transform(right, Hocon::ConfigValueType::LIST)
31
89
  end
32
90
 
33
91
  # Since this depends on the type of two instances, I couldn't think
@@ -38,9 +96,13 @@ class Hocon::Impl::ConfigConcatenation < Hocon::Impl::AbstractConfigValue
38
96
  joined = right.with_fallback(left)
39
97
  elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(SimpleConfigList))
40
98
  joined = left.concatenate(right)
99
+ elsif (left.is_a?(SimpleConfigList) || left.is_a?(ConfigObject)) &&
100
+ is_ignored_whitespace(right)
101
+ joined = left
102
+ # it should be impossible that left is whitespace and right is a list or object
41
103
  elsif (left.is_a?(Hocon::Impl::ConfigConcatenation)) ||
42
104
  (right.is_a?(Hocon::Impl::ConfigConcatenation))
43
- raise ConfigBugError, "unflattened ConfigConcatenation"
105
+ raise ConfigBugOrBrokenError, "unflattened ConfigConcatenation"
44
106
  elsif (left.is_a?(Unmergeable)) || (right.is_a?(Unmergeable))
45
107
  # leave joined=null, cannot join
46
108
  else
@@ -50,10 +112,10 @@ class Hocon::Impl::ConfigConcatenation < Hocon::Impl::AbstractConfigValue
50
112
  if s1.nil? || s2.nil?
51
113
  raise ConfigWrongTypeError.new(left.origin,
52
114
  "Cannot concatenate object or list with a non-object-or-list, #{left} " +
53
- "and #{right} are not compatible")
115
+ "and #{right} are not compatible", nil)
54
116
  else
55
117
  joined_origin = SimpleConfigOrigin.merge_origins([left.origin, right.origin])
56
- joined = Hocon::Impl::ConfigString.new(joined_origin, s1 + s2)
118
+ joined = Hocon::Impl::ConfigString::Quoted.new(joined_origin, s1 + s2)
57
119
  end
58
120
  end
59
121
 
@@ -98,39 +160,126 @@ class Hocon::Impl::ConfigConcatenation < Hocon::Impl::AbstractConfigValue
98
160
  elsif consolidated.length == 1
99
161
  consolidated[0]
100
162
  else
101
- merged_origin = SimpleConfigOrigin.merge_origins(consolidated)
163
+ merged_origin = SimpleConfigOrigin.merge_value_origins(consolidated)
102
164
  Hocon::Impl::ConfigConcatenation.new(merged_origin, consolidated)
103
165
  end
104
166
  end
105
167
 
106
-
107
- def initialize(origin, pieces)
108
- super(origin)
109
- @pieces = pieces
110
-
111
- if pieces.size < 2
112
- raise ConfigBugError, "Created concatenation with less than 2 items: #{self}"
168
+ def resolve_substitutions(context, source)
169
+ if Hocon::Impl::ConfigImpl.trace_substitution_enabled
170
+ indent = context.depth + 2
171
+ Hocon::Impl::ConfigImpl.trace("concatenation has #{@pieces.size} pieces",
172
+ indent - 1)
173
+ count = 0
174
+ @pieces.each { |v|
175
+ Hocon::Impl::ConfigImpl.trace("#{count}: #{v}", count)
176
+ count += 1
177
+ }
113
178
  end
114
179
 
115
- had_unmergeable = false
116
- pieces.each do |p|
117
- if p.is_a?(Hocon::Impl::ConfigConcatenation)
118
- raise ConfigBugError, "ConfigConcatenation should never be nested: #{self}"
180
+ # Right now there's no reason to pushParent here because the
181
+ # content of ConfigConcatenation should not need to replaceChild,
182
+ # but if it did we'd have to do this.
183
+ source_with_parent = source
184
+ new_context = context
185
+
186
+ resolved = []
187
+ @pieces.each { |p|
188
+ # to concat into a string we have to do a full resolve,
189
+ # so unrestrict the context, then put restriction back afterward
190
+ restriction = new_context.restrict_to_child
191
+ result = new_context.unrestricted
192
+ .resolve(p, source_with_parent)
193
+ r = result.value
194
+ new_context = result.context.restrict(restriction)
195
+ if Hocon::Impl::ConfigImpl.trace_substitution_enabled
196
+ Hocon::Impl::ConfigImpl.trace("resolved concat piece to #{r}",
197
+ context.depth)
119
198
  end
120
- if p.is_a?(Unmergeable)
121
- had_unmergeable = true
199
+
200
+ if r
201
+ resolved << r
122
202
  end
203
+ # otherwise, it was optional ... omit
204
+ }
205
+
206
+ # now need to concat everything
207
+ joined = self.class.consolidate(resolved)
208
+ # if unresolved is allowed we can just become another
209
+ # ConfigConcatenation
210
+ if joined.size > 1 and context.options.allow_unresolved
211
+ Hocon::Impl::ResolveResult.make(new_context, Hocon::Impl::ConfigConcatenation.new(origin, joined))
212
+ elsif joined.empty?
213
+ # we had just a list of optional references using ${?}
214
+ Hocon::Impl::ResolveResult.make(new_context, nil)
215
+ elsif joined.size == 1
216
+ Hocon::Impl::ResolveResult.make(new_context, joined[0])
217
+ else
218
+ raise ConfigBugOrBrokenError.new(
219
+ "Bug in the library; resolved list was joined to too many values: #{joined}")
123
220
  end
221
+ end
124
222
 
125
- unless had_unmergeable
126
- raise ConfigBugError, "Created concatenation without an unmergeable in it: #{self}"
223
+ def resolve_status
224
+ ResolveStatus::UNRESOLVED
225
+ end
226
+
227
+ def replace_child(child, replacement)
228
+ new_pieces = replace_child_in_list(@pieces, child, replacement)
229
+ if new_pieces == nil
230
+ nil
231
+ else
232
+ self.class.new(origin, new_pieces)
127
233
  end
128
234
  end
129
235
 
130
- def ignores_fallbacks?
131
- # we can never ignore fallbacks because if a child ConfigReference
132
- # is self-referential we have to look lower in the merge stack
133
- # for its value.
134
- false
236
+ def has_descendant?(descendant)
237
+ has_descendant_in_list?(@pieces, descendant)
238
+ end
239
+
240
+ # when you graft a substitution into another object,
241
+ # you have to prefix it with the location in that object
242
+ # where you grafted it; but save prefixLength so
243
+ # system property and env variable lookups don 't get
244
+ # broken.
245
+ def relativized(prefix)
246
+ new_pieces = []
247
+ @pieces.each { |p|
248
+ new_pieces << p.relativized(prefix)
249
+ }
250
+ self.class.new(origin, new_pieces)
251
+ end
252
+
253
+ def can_equal(other)
254
+ other.is_a? Hocon::Impl::ConfigConcatenation
255
+ end
256
+
257
+ def ==(other)
258
+ if other.is_a? Hocon::Impl::ConfigConcatenation
259
+ can_equal(other) && @pieces == other.pieces
260
+ else
261
+ false
262
+ end
263
+ end
264
+
265
+ def hash
266
+ # note that "origin" is deliberately NOT part of equality
267
+ @pieces.hash
268
+ end
269
+
270
+ def render_value_to_sb(sb, indent, at_root, options)
271
+ @pieces.each do |piece|
272
+ piece.render_value_to_sb(sb, indent, at_root, options)
273
+ end
274
+ end
275
+
276
+ private
277
+
278
+ def not_resolved
279
+ ConfigNotResolvedError.new("need to Config#resolve(), see the API docs for Config#resolve(); substitution not resolved: #{self}")
280
+ end
281
+
282
+ def self.is_ignored_whitespace(value)
283
+ return value.is_a?(ConfigString) && !value.was_quoted?
135
284
  end
136
- end
285
+ end
@@ -0,0 +1,329 @@
1
+ require 'hocon/impl'
2
+ require 'hocon/impl/replaceable_merge_stack'
3
+ require 'hocon/impl/config_delayed_merge_object'
4
+ require 'hocon/impl/config_impl'
5
+ require 'hocon/impl/resolve_result'
6
+ require 'hocon/impl/abstract_config_value'
7
+
8
+ #
9
+ # The issue here is that we want to first merge our stack of config files, and
10
+ # then we want to evaluate substitutions. But if two substitutions both expand
11
+ # to an object, we might need to merge those two objects. Thus, we can't ever
12
+ # "override" a substitution when we do a merge; instead we have to save the
13
+ # stack of values that should be merged, and resolve the merge when we evaluate
14
+ # substitutions.
15
+ #
16
+ class Hocon::Impl::ConfigDelayedMerge
17
+ include Hocon::Impl::Unmergeable
18
+ include Hocon::Impl::ReplaceableMergeStack
19
+ include Hocon::Impl::AbstractConfigValue
20
+
21
+ ConfigImpl = Hocon::Impl::ConfigImpl
22
+ ResolveResult = Hocon::Impl::ResolveResult
23
+
24
+ def initialize(origin, stack)
25
+ super(origin)
26
+ @stack = stack
27
+
28
+ if stack.empty?
29
+ raise Hocon::ConfigError::ConfigBugOrBrokenError.new("creating empty delayed merge value", nil)
30
+ end
31
+
32
+ stack.each do |v|
33
+ if v.is_a?(Hocon::Impl::ConfigDelayedMerge) || v.is_a?(Hocon::Impl::ConfigDelayedMergeObject)
34
+ error_message = "placed nested DelayedMerge in a ConfigDelayedMerge, should have consolidated stack"
35
+ raise Hocon::ConfigError::ConfigBugOrBrokenError.new(error_message, nil)
36
+ end
37
+ end
38
+ end
39
+
40
+ attr_reader :stack
41
+
42
+
43
+ def value_type
44
+ error_message = "called value_type() on value with unresolved substitutions, need to Config#resolve() first, see API docs"
45
+ raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
46
+ end
47
+
48
+ def unwrapped
49
+ error_message = "called unwrapped() on value with unresolved substitutions, need to Config#resolve() first, see API docs"
50
+ raise Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
51
+ end
52
+
53
+ def resolve_substitutions(context, source)
54
+ self.class.resolve_substitutions(self, stack, context, source)
55
+ end
56
+
57
+ def self.resolve_substitutions(replaceable, stack, context, source)
58
+ if ConfigImpl.trace_substitution_enabled
59
+ ConfigImpl.trace("delayed merge stack has #{stack.size} items:", context.depth)
60
+ count = 0
61
+ stack.each do |v|
62
+ ConfigImpl.trace("#{count}: #{v}", context.depth)
63
+ count += 1
64
+ end
65
+ end
66
+
67
+ # to resolve substitutions, we need to recursively resolve
68
+ # the stack of stuff to merge, and merge the stack so
69
+ # we won't be a delayed merge anymore. If restrictToChildOrNull
70
+ # is non-null, or resolve options allow partial resolves,
71
+ # we may remain a delayed merge though.
72
+
73
+ new_context = context
74
+ count = 0
75
+ merged = nil
76
+ stack.each do |stack_end|
77
+ # the end value may or may not be resolved already
78
+
79
+ if stack_end.is_a?(Hocon::Impl::ReplaceableMergeStack)
80
+ raise ConfigBugOrBrokenError, "A delayed merge should not contain another one: #{replaceable}"
81
+ elsif stack_end.is_a?(Hocon::Impl::Unmergeable)
82
+ # the remainder could be any kind of value, including another
83
+ # ConfigDelayedMerge
84
+ remainder = replaceable.make_replacement(context, count + 1)
85
+
86
+ if ConfigImpl.trace_substitution_enabled
87
+ ConfigImpl.trace("remainder portion: #{remainder}", new_context.depth)
88
+ end
89
+
90
+ # If, while resolving 'end' we come back to the same
91
+ # merge stack, we only want to look _below_ 'end'
92
+ # in the stack. So we arrange to replace the
93
+ # ConfigDelayedMerge with a value that is only
94
+ # the remainder of the stack below this one.
95
+
96
+ if ConfigImpl.trace_substitution_enabled
97
+ ConfigImpl.trace("building sourceForEnd", new_context.depth)
98
+ end
99
+
100
+ # we resetParents() here because we'll be resolving "end"
101
+ # against a root which does NOT contain "end"
102
+ source_for_end = source.replace_within_current_parent(replaceable, remainder)
103
+
104
+ if ConfigImpl.trace_substitution_enabled
105
+ ConfigImpl.trace(" sourceForEnd before reset parents but after replace: #{source_for_end}", new_context.depth)
106
+ end
107
+
108
+ source_for_end = source_for_end.reset_parents
109
+ else
110
+ if ConfigImpl.trace_substitution_enabled
111
+ ConfigImpl.trace("will resolve end against the original source with parent pushed",
112
+ new_context.depth)
113
+ end
114
+
115
+ source_for_end = source.push_parent(replaceable)
116
+ end
117
+
118
+ if ConfigImpl.trace_substitution_enabled
119
+ ConfigImpl.trace("sourceForEnd =#{source_for_end}", new_context.depth)
120
+ end
121
+
122
+ if ConfigImpl.trace_substitution_enabled
123
+ ConfigImpl.trace("Resolving highest-priority item in delayed merge #{stack_end}" +
124
+ " against #{source_for_end} endWasRemoved=#{(source != source_for_end)}")
125
+ end
126
+
127
+ result = new_context.resolve(stack_end, source_for_end)
128
+ resolved_end = result.value
129
+ new_context = result.context
130
+
131
+ if ! resolved_end.nil?
132
+ if merged.nil?
133
+ merged = resolved_end
134
+ else
135
+ if ConfigImpl.trace_substitution_enabled
136
+ ConfigImpl.trace("merging #{merged} with fallback #{resolved_end}",
137
+ new_context.depth + 1)
138
+ end
139
+ merged = merged.with_fallback(resolved_end)
140
+ end
141
+ end
142
+
143
+ count += 1
144
+
145
+ if ConfigImpl.trace_substitution_enabled
146
+ ConfigImpl.trace("stack merged, yielding: #{merged}",
147
+ new_context.depth)
148
+ end
149
+ end
150
+
151
+ ResolveResult.make(new_context, merged)
152
+ end
153
+
154
+ def make_replacement(context, skipping)
155
+ self.class.make_replacement(context, @stack, skipping)
156
+ end
157
+
158
+ # static method also used by ConfigDelayedMergeObject; end may be null
159
+ def self.make_replacement(context, stack, skipping)
160
+ sub_stack = stack.slice(skipping..stack.size)
161
+
162
+ if sub_stack.empty?
163
+ if ConfigImpl.trace_substitution_enabled
164
+ ConfigImpl.trace("Nothing else in the merge stack, replacing with null", context.depth)
165
+ return nil
166
+ end
167
+ else
168
+ # generate a new merge stack from only the remaining items
169
+ merged = nil
170
+ sub_stack.each do |v|
171
+ if merged.nil?
172
+ merged = v
173
+ else
174
+ merged = merged.with_fallback(v)
175
+ end
176
+ end
177
+ merged
178
+ end
179
+ end
180
+
181
+ def resolve_status
182
+ Hocon::Impl::ResolveStatus::UNRESOLVED
183
+ end
184
+
185
+ def replace_child(child, replacement)
186
+ new_stack = replace_child_in_list(stack, child, replacement)
187
+ if new_stack.nil?
188
+ nil
189
+ else
190
+ self.class.new(origin, new_stack)
191
+ end
192
+ end
193
+
194
+ def has_descendant?(descendant)
195
+ Hocon::Impl::AbstractConfigValue.has_descendant_in_list?(stack, descendant)
196
+ end
197
+
198
+ def relativized(prefix)
199
+ new_stack = stack.map { |o| o.relativized(prefix) }
200
+ self.class.new(origin, new_stack)
201
+ end
202
+
203
+ # static utility shared with ConfigDelayedMergeObject
204
+ def self.stack_ignores_fallbacks?(stack)
205
+ last = stack[-1]
206
+ last.ignores_fallbacks?
207
+ end
208
+
209
+ def ignores_fallbacks?
210
+ self.class.stack_ignores_fallbacks?(stack)
211
+ end
212
+
213
+ def new_copy(new_origin)
214
+ self.class.new(new_origin, stack)
215
+ end
216
+
217
+ def merged_with_the_unmergeable(fallback)
218
+ merged_stack_with_the_unmergeable(stack, fallback)
219
+ end
220
+
221
+ def merged_with_object(fallback)
222
+ merged_stack_with_object(stack, fallback)
223
+ end
224
+
225
+ def merged_with_non_object(fallback)
226
+ merged_stack_with_non_object(stack, fallback)
227
+ end
228
+
229
+ def unmerged_values
230
+ stack
231
+ end
232
+
233
+ def can_equal(other)
234
+ other.is_a? Hocon::Impl::ConfigDelayedMerge
235
+ end
236
+
237
+ def ==(other)
238
+ # note that "origin" is deliberately NOT part of equality
239
+ if other.is_a? Hocon::Impl::ConfigDelayedMerge
240
+ can_equal(other) && (@stack == other.stack || @stack.equal?(other.stack))
241
+ else
242
+ false
243
+ end
244
+ end
245
+
246
+ def hash
247
+ # note that "origin" is deliberately NOT part of equality
248
+ @stack.hash
249
+ end
250
+
251
+ def render_to_sb(sb, indent, at_root, at_key, options)
252
+ self.class.render_value_to_sb_from_stack(stack, sb, indent, at_root, at_key, options)
253
+ end
254
+
255
+ # static method also used by ConfigDelayedMergeObject.
256
+ def self.render_value_to_sb_from_stack(stack, sb, indent, at_root, at_key, options)
257
+ comment_merge = options.comments
258
+
259
+ if comment_merge
260
+ sb << "# unresolved merge of #{stack.size} values follows (\n"
261
+ if at_key.nil?
262
+ self.indent(sb, indent, options)
263
+ sb << "# this unresolved merge will not be parseable because it's at the root of the object\n"
264
+ self.indent(sb, indent, options)
265
+ sb << "# the HOCON format has no way to list multiple root objects in a single file\n"
266
+ end
267
+ end
268
+
269
+ reversed = stack.reverse
270
+
271
+ i = 0
272
+
273
+ reversed.each do |v|
274
+ if comment_merge
275
+ self.indent(sb, indent, options)
276
+ if !at_key.nil?
277
+ rendered_key = Hocon::Impl::ConfigImplUtil.render_json_string(at_key)
278
+ sb << "# unmerged value #{i} for key #{rendered_key}"
279
+ else
280
+ sb << "# unmerged value #{i} from "
281
+ end
282
+ i += 1
283
+
284
+ sb << v.origin.description
285
+ sb << "\n"
286
+
287
+ v.origin.comments.each do |comment|
288
+ self.indent(sb, indent, options)
289
+ sb << "# "
290
+ sb << comment
291
+ sb << "\n"
292
+ end
293
+ end
294
+ Hocon::Impl::AbstractConfigValue.indent(sb, indent, options)
295
+
296
+ if !at_key.nil?
297
+ sb << Hocon::Impl::ConfigImplUtil.render_json_string(at_key)
298
+ if options.formatted
299
+ sb << " : "
300
+ else
301
+ sb << ":"
302
+ end
303
+ end
304
+
305
+ v.render_value_to_sb(sb, indent, at_root, options)
306
+ sb << ","
307
+
308
+ if options.formatted
309
+ sb.append "\n"
310
+ end
311
+ end
312
+
313
+ # chop comma or newline
314
+ # couldn't figure out a better way to chop characters off of the end of
315
+ # the StringIO. This relies on making sure that, prior to returning the
316
+ # final string, we take a substring that ends at sb.pos.
317
+ sb.pos = sb.pos - 1
318
+ if options.formatted
319
+ sb.pos = sb.pos - 1
320
+ sb << "\n"
321
+ end
322
+
323
+ if comment_merge
324
+ self.indent(sb, indent, options)
325
+ sb << "# ) end of unresolved merge\n"
326
+ end
327
+ end
328
+
329
+ end