hocon 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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