hocon 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +4 -2
- data/lib/hocon.rb +2 -0
- data/lib/hocon/config.rb +1010 -0
- data/lib/hocon/config_error.rb +32 -2
- data/lib/hocon/config_factory.rb +46 -0
- data/lib/hocon/config_include_context.rb +49 -0
- data/lib/hocon/config_includer_file.rb +27 -0
- data/lib/hocon/config_list.rb +49 -0
- data/lib/hocon/config_mergeable.rb +74 -0
- data/lib/hocon/config_object.rb +144 -1
- data/lib/hocon/config_parse_options.rb +33 -9
- data/lib/hocon/config_parseable.rb +51 -0
- data/lib/hocon/config_render_options.rb +4 -2
- data/lib/hocon/config_resolve_options.rb +31 -0
- data/lib/hocon/config_syntax.rb +5 -2
- data/lib/hocon/config_util.rb +73 -0
- data/lib/hocon/config_value.rb +122 -0
- data/lib/hocon/config_value_factory.rb +66 -2
- data/lib/hocon/config_value_type.rb +5 -2
- data/lib/hocon/impl.rb +2 -0
- data/lib/hocon/impl/abstract_config_node.rb +29 -0
- data/lib/hocon/impl/abstract_config_node_value.rb +11 -0
- data/lib/hocon/impl/abstract_config_object.rb +148 -42
- data/lib/hocon/impl/abstract_config_value.rb +251 -11
- data/lib/hocon/impl/array_iterator.rb +19 -0
- data/lib/hocon/impl/config_boolean.rb +7 -1
- data/lib/hocon/impl/config_concatenation.rb +177 -28
- data/lib/hocon/impl/config_delayed_merge.rb +329 -0
- data/lib/hocon/impl/config_delayed_merge_object.rb +274 -0
- data/lib/hocon/impl/config_document_parser.rb +647 -0
- data/lib/hocon/impl/config_double.rb +44 -0
- data/lib/hocon/impl/config_impl.rb +143 -19
- data/lib/hocon/impl/config_impl_util.rb +18 -0
- data/lib/hocon/impl/config_include_kind.rb +10 -0
- data/lib/hocon/impl/config_int.rb +13 -1
- data/lib/hocon/impl/config_node_array.rb +11 -0
- data/lib/hocon/impl/config_node_comment.rb +19 -0
- data/lib/hocon/impl/config_node_complex_value.rb +54 -0
- data/lib/hocon/impl/config_node_concatenation.rb +11 -0
- data/lib/hocon/impl/config_node_field.rb +81 -0
- data/lib/hocon/impl/config_node_include.rb +33 -0
- data/lib/hocon/impl/config_node_object.rb +276 -0
- data/lib/hocon/impl/config_node_path.rb +48 -0
- data/lib/hocon/impl/config_node_root.rb +60 -0
- data/lib/hocon/impl/config_node_simple_value.rb +42 -0
- data/lib/hocon/impl/config_node_single_token.rb +17 -0
- data/lib/hocon/impl/config_null.rb +15 -7
- data/lib/hocon/impl/config_number.rb +43 -4
- data/lib/hocon/impl/config_parser.rb +403 -0
- data/lib/hocon/impl/config_reference.rb +142 -0
- data/lib/hocon/impl/config_string.rb +55 -7
- data/lib/hocon/impl/container.rb +29 -0
- data/lib/hocon/impl/default_transformer.rb +24 -15
- data/lib/hocon/impl/from_map_mode.rb +3 -1
- data/lib/hocon/impl/full_includer.rb +2 -0
- data/lib/hocon/impl/memo_key.rb +42 -0
- data/lib/hocon/impl/mergeable_value.rb +8 -0
- data/lib/hocon/impl/origin_type.rb +8 -2
- data/lib/hocon/impl/parseable.rb +455 -91
- data/lib/hocon/impl/path.rb +181 -59
- data/lib/hocon/impl/path_builder.rb +24 -3
- data/lib/hocon/impl/path_parser.rb +280 -0
- data/lib/hocon/impl/replaceable_merge_stack.rb +22 -0
- data/lib/hocon/impl/resolve_context.rb +254 -0
- data/lib/hocon/impl/resolve_memos.rb +21 -0
- data/lib/hocon/impl/resolve_result.rb +39 -0
- data/lib/hocon/impl/resolve_source.rb +354 -0
- data/lib/hocon/impl/resolve_status.rb +3 -1
- data/lib/hocon/impl/simple_config.rb +264 -10
- data/lib/hocon/impl/simple_config_document.rb +48 -0
- data/lib/hocon/impl/simple_config_list.rb +282 -8
- data/lib/hocon/impl/simple_config_object.rb +424 -88
- data/lib/hocon/impl/simple_config_origin.rb +263 -71
- data/lib/hocon/impl/simple_include_context.rb +31 -1
- data/lib/hocon/impl/simple_includer.rb +196 -1
- data/lib/hocon/impl/substitution_expression.rb +38 -0
- data/lib/hocon/impl/token.rb +17 -4
- data/lib/hocon/impl/token_type.rb +6 -2
- data/lib/hocon/impl/tokenizer.rb +339 -109
- data/lib/hocon/impl/tokens.rb +330 -79
- data/lib/hocon/impl/unmergeable.rb +14 -1
- data/lib/hocon/impl/unsupported_operation_error.rb +6 -0
- data/lib/hocon/impl/url.rb +37 -0
- data/lib/hocon/parser.rb +7 -0
- data/lib/hocon/parser/config_document.rb +92 -0
- data/lib/hocon/parser/config_document_factory.rb +36 -0
- data/lib/hocon/parser/config_node.rb +30 -0
- metadata +67 -43
- data/lib/hocon/impl/config_float.rb +0 -13
- data/lib/hocon/impl/parser.rb +0 -977
- data/lib/hocon/impl/properties_parser.rb +0 -83
@@ -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
|
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
|
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
|
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.
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
121
|
-
|
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
|
-
|
126
|
-
|
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
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|