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.
- 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
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon/impl'
|
4
|
+
require 'hocon/impl/container'
|
5
|
+
require 'hocon/config_error'
|
6
|
+
|
7
|
+
#
|
8
|
+
# Implemented by a merge stack (ConfigDelayedMerge, ConfigDelayedMergeObject)
|
9
|
+
# that replaces itself during substitution resolution in order to implement
|
10
|
+
# "look backwards only" semantics.
|
11
|
+
#
|
12
|
+
module Hocon::Impl::ReplaceableMergeStack
|
13
|
+
include Hocon::Impl::Container
|
14
|
+
|
15
|
+
#
|
16
|
+
# Make a replacement for this object skipping the given number of elements
|
17
|
+
# which are lower in merge priority.
|
18
|
+
#
|
19
|
+
def make_replacement(context, skipping)
|
20
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `ReplaceableMergeStack` must implement `make_replacement` (#{self.class})"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon'
|
4
|
+
require 'hocon/config_error'
|
5
|
+
require 'hocon/impl/resolve_source'
|
6
|
+
require 'hocon/impl/resolve_memos'
|
7
|
+
require 'hocon/impl/memo_key'
|
8
|
+
require 'hocon/impl/abstract_config_value'
|
9
|
+
require 'hocon/impl/config_impl'
|
10
|
+
|
11
|
+
class Hocon::Impl::ResolveContext
|
12
|
+
|
13
|
+
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
|
14
|
+
NotPossibleToResolve = Hocon::Impl::AbstractConfigValue::NotPossibleToResolve
|
15
|
+
|
16
|
+
attr_reader :restrict_to_child
|
17
|
+
|
18
|
+
def initialize(memos, options, restrict_to_child, resolve_stack, cycle_markers)
|
19
|
+
@memos = memos
|
20
|
+
@options = options
|
21
|
+
@restrict_to_child = restrict_to_child
|
22
|
+
@resolve_stack = resolve_stack
|
23
|
+
@cycle_markers = cycle_markers
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.new_cycle_markers
|
27
|
+
# This looks crazy, but wtf else should we do with
|
28
|
+
# return Collections.newSetFromMap(new IdentityHashMap<AbstractConfigValue, Boolean>());
|
29
|
+
Set.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_cycle_marker(value)
|
33
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
34
|
+
Hocon::Impl::ConfigImpl.trace("++ Cycle marker #{value}@#{value.hash}",
|
35
|
+
depth)
|
36
|
+
end
|
37
|
+
if @cycle_markers.include?(value)
|
38
|
+
raise ConfigBugOrBrokenError.new("Added cycle marker twice " + value)
|
39
|
+
end
|
40
|
+
copy = self.class.new_cycle_markers
|
41
|
+
copy.merge(@cycle_markers)
|
42
|
+
copy.add(value)
|
43
|
+
self.class.new(@memos, @options, @restrict_to_child, @resolve_stack, copy)
|
44
|
+
end
|
45
|
+
|
46
|
+
def remove_cycle_marker(value)
|
47
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
48
|
+
Hocon::Impl::ConfigImpl.trace("-- Cycle marker #{value}@#{value.hash}",
|
49
|
+
depth)
|
50
|
+
end
|
51
|
+
|
52
|
+
copy = self.class.new_cycle_markers
|
53
|
+
copy.merge(@cycle_markers)
|
54
|
+
copy.delete(value)
|
55
|
+
self.class.new(@memos, @options, @restrict_to_child, @resolve_stack, copy)
|
56
|
+
end
|
57
|
+
|
58
|
+
def memoize(key, value)
|
59
|
+
changed = @memos.put(key, value)
|
60
|
+
self.class.new(changed, @options, @restrict_to_child, @resolve_stack, @cycle_markers)
|
61
|
+
end
|
62
|
+
|
63
|
+
def options
|
64
|
+
@options
|
65
|
+
end
|
66
|
+
|
67
|
+
def is_restricted_to_child
|
68
|
+
@restrict_to_child != nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def restrict(restrict_to)
|
72
|
+
if restrict_to.equal?(@restrict_to_child)
|
73
|
+
self
|
74
|
+
else
|
75
|
+
Hocon::Impl::ResolveContext.new(@memos, @options, restrict_to, @resolve_stack, @cycle_markers)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def unrestricted
|
80
|
+
restrict(nil)
|
81
|
+
end
|
82
|
+
|
83
|
+
def resolve(original, source)
|
84
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
85
|
+
Hocon::Impl::ConfigImpl.trace(
|
86
|
+
"resolving #{original} restrict_to_child=#{@restrict_to_child} in #{source}",
|
87
|
+
depth)
|
88
|
+
end
|
89
|
+
push_trace(original).real_resolve(original, source).pop_trace
|
90
|
+
end
|
91
|
+
|
92
|
+
def real_resolve(original, source)
|
93
|
+
# a fully-resolved (no restrict_to_child) object can satisfy a
|
94
|
+
# request for a restricted object, so always check that first.
|
95
|
+
full_key = Hocon::Impl::MemoKey.new(original, nil)
|
96
|
+
restricted_key = nil
|
97
|
+
|
98
|
+
cached = @memos.get(full_key)
|
99
|
+
|
100
|
+
# but if there was no fully-resolved object cached, we'll only
|
101
|
+
# compute the restrictToChild object so use a more limited
|
102
|
+
# memo key
|
103
|
+
if cached == nil && is_restricted_to_child
|
104
|
+
restricted_key = Hocon::Impl::MemoKey.new(original, @restrict_to_child)
|
105
|
+
cached = @memos.get(restricted_key)
|
106
|
+
end
|
107
|
+
|
108
|
+
if cached != nil
|
109
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
110
|
+
Hocon::Impl::ConfigImpl.trace(
|
111
|
+
"using cached resolution #{cached} for #{original} restrict_to_child #{@restrict_to_child}",
|
112
|
+
depth)
|
113
|
+
end
|
114
|
+
Hocon::Impl::ResolveResult.make(self, cached)
|
115
|
+
else
|
116
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
117
|
+
Hocon::Impl::ConfigImpl.trace(
|
118
|
+
"not found in cache, resolving #{original}@#{original.hash}",
|
119
|
+
depth)
|
120
|
+
end
|
121
|
+
|
122
|
+
if @cycle_markers.include?(original)
|
123
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
124
|
+
Hocon::Impl::ConfigImpl.trace(
|
125
|
+
"Cycle detected, can't resolve; #{original}@#{original.hash}",
|
126
|
+
depth)
|
127
|
+
end
|
128
|
+
raise NotPossibleToResolve.new(self)
|
129
|
+
end
|
130
|
+
|
131
|
+
result = original.resolve_substitutions(self, source)
|
132
|
+
resolved = result.value
|
133
|
+
|
134
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
135
|
+
Hocon::Impl::ConfigImpl.trace(
|
136
|
+
"resolved to #{resolved}@#{resolved.hash} from #{original}@#{resolved.hash}",
|
137
|
+
depth)
|
138
|
+
end
|
139
|
+
|
140
|
+
with_memo = result.context
|
141
|
+
|
142
|
+
if resolved == nil || resolved.resolve_status == Hocon::Impl::ResolveStatus::RESOLVED
|
143
|
+
# if the resolved object is fully resolved by resolving
|
144
|
+
# only the restrictToChildOrNull, then it can be cached
|
145
|
+
# under fullKey since the child we were restricted to
|
146
|
+
# turned out to be the only unresolved thing.
|
147
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
148
|
+
Hocon::Impl::ConfigImpl.trace(
|
149
|
+
"caching #{full_key} result #{resolved}",
|
150
|
+
depth)
|
151
|
+
end
|
152
|
+
|
153
|
+
with_memo = with_memo.memoize(full_key, resolved)
|
154
|
+
else
|
155
|
+
# if we have an unresolved object then either we did a
|
156
|
+
# partial resolve restricted to a certain child, or we are
|
157
|
+
# allowing incomplete resolution, or it's a bug.
|
158
|
+
if is_restricted_to_child
|
159
|
+
if restricted_key == nil
|
160
|
+
raise ConfigBugOrBrokenError.new("restricted_key should not be null here")
|
161
|
+
end
|
162
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
163
|
+
Hocon::Impl::ConfigImpl.trace(
|
164
|
+
"caching #{restricted_key} result #{resolved}",
|
165
|
+
depth)
|
166
|
+
end
|
167
|
+
|
168
|
+
with_memo = with_memo.memoize(restricted_key, resolved)
|
169
|
+
elsif @options.allow_unresolved
|
170
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
171
|
+
Hocon::Impl::ConfigImpl.trace(
|
172
|
+
"caching #{full_key} result #{resolved}",
|
173
|
+
depth)
|
174
|
+
end
|
175
|
+
|
176
|
+
with_memo = with_memo.memoize(full_key, resolved)
|
177
|
+
else
|
178
|
+
raise ConfigBugOrBrokenError.new(
|
179
|
+
"resolve_substitutions did not give us a resolved object")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
Hocon::Impl::ResolveResult.make(with_memo, resolved)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# This method is a translation of the constructor in the Java version with signature
|
187
|
+
# ResolveContext(ConfigResolveOptions options, Path restrictToChild)
|
188
|
+
def self.construct(options, restrict_to_child)
|
189
|
+
context = self.new(Hocon::Impl::ResolveMemos.new,
|
190
|
+
options,
|
191
|
+
restrict_to_child,
|
192
|
+
[],
|
193
|
+
new_cycle_markers)
|
194
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
195
|
+
Hocon::Impl::ConfigImpl.trace(
|
196
|
+
"ResolveContext restrict to child #{restrict_to_child}", context.depth)
|
197
|
+
end
|
198
|
+
context
|
199
|
+
end
|
200
|
+
|
201
|
+
def trace_string
|
202
|
+
separator = ", "
|
203
|
+
sb = ""
|
204
|
+
@resolve_stack.each { |value|
|
205
|
+
if value.instance_of?(Hocon::Impl::ConfigReference)
|
206
|
+
sb << value.expression.to_s
|
207
|
+
sb << separator
|
208
|
+
end
|
209
|
+
}
|
210
|
+
if sb.length > 0
|
211
|
+
sb.chomp!(separator)
|
212
|
+
end
|
213
|
+
sb
|
214
|
+
end
|
215
|
+
|
216
|
+
def depth
|
217
|
+
if @resolve_stack.size > 30
|
218
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError.new("resolve getting too deep")
|
219
|
+
end
|
220
|
+
@resolve_stack.size
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.resolve(value, root, options)
|
224
|
+
source = Hocon::Impl::ResolveSource.new(root)
|
225
|
+
context = construct(options, nil)
|
226
|
+
begin
|
227
|
+
context.resolve(value, source).value
|
228
|
+
rescue NotPossibleToResolve => e
|
229
|
+
# ConfigReference was supposed to catch NotPossibleToResolve
|
230
|
+
raise ConfigBugOrBrokenError(
|
231
|
+
"NotPossibleToResolve was thrown from an outermost resolve", e)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def pop_trace
|
236
|
+
copy = @resolve_stack.clone
|
237
|
+
old = copy.delete_at(@resolve_stack.size - 1)
|
238
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
239
|
+
Hocon::Impl::ConfigImpl.trace("popped trace #{old}", depth - 1)
|
240
|
+
end
|
241
|
+
Hocon::Impl::ResolveContext.new(@memos, @options, @restrict_to_child, copy, @cycle_markers)
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
def push_trace(value)
|
247
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
248
|
+
Hocon::Impl::ConfigImpl.trace("pushing trace #{value}", depth)
|
249
|
+
end
|
250
|
+
copy = @resolve_stack.clone
|
251
|
+
copy << value
|
252
|
+
Hocon::Impl::ResolveContext.new(@memos, @options, @restrict_to_child, copy, @cycle_markers)
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon'
|
4
|
+
require 'hocon/impl'
|
5
|
+
|
6
|
+
class Hocon::Impl::ResolveMemos
|
7
|
+
|
8
|
+
def initialize(memos = {})
|
9
|
+
@memos = memos
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(key)
|
13
|
+
@memos[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def put(key, value)
|
17
|
+
copy = @memos.clone
|
18
|
+
copy[key] = value
|
19
|
+
Hocon::Impl::ResolveMemos.new(copy)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon'
|
4
|
+
require 'hocon/impl'
|
5
|
+
|
6
|
+
# value is allowed to be null
|
7
|
+
class Hocon::Impl::ResolveResult
|
8
|
+
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
|
9
|
+
|
10
|
+
attr_accessor :context, :value
|
11
|
+
|
12
|
+
def initialize(context, value)
|
13
|
+
@context = context
|
14
|
+
@value = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.make(context, value)
|
18
|
+
self.new(context, value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_object_result
|
22
|
+
unless @value.is_a?(Hocon::Impl::AbstractConfigObject)
|
23
|
+
raise ConfigBugOrBrokenError.new("Expecting a resolve result to be an object, but it was #{@value}")
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def as_value_result
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def pop_trace
|
33
|
+
self.class.make(@context.pop_trace, value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"ResolveResult(#{@value})"
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,354 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon'
|
4
|
+
require 'hocon/config_error'
|
5
|
+
require 'hocon/impl'
|
6
|
+
require 'hocon/impl/config_impl'
|
7
|
+
require 'hocon/impl/container'
|
8
|
+
|
9
|
+
class Hocon::Impl::ResolveSource
|
10
|
+
|
11
|
+
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
|
12
|
+
ConfigNotResolvedError = Hocon::ConfigError::ConfigNotResolvedError
|
13
|
+
|
14
|
+
# 'path_from_root' is used for knowing the chain of parents we used to get here.
|
15
|
+
# null if we should assume we are not a descendant of the root.
|
16
|
+
# the root itself should be a node in this if non-null.
|
17
|
+
|
18
|
+
attr_accessor :root, :path_from_root
|
19
|
+
|
20
|
+
def initialize(root, path_from_root = nil)
|
21
|
+
@root = root
|
22
|
+
@path_from_root = path_from_root
|
23
|
+
end
|
24
|
+
|
25
|
+
# as a side effect, findInObject() will have to resolve all parents of the
|
26
|
+
# child being peeked, but NOT the child itself.Caller has to resolve
|
27
|
+
# the child itself if needed.ValueWithPath.value can be null but
|
28
|
+
# the ValueWithPath instance itself should not be.
|
29
|
+
def find_in_object(obj, context, path)
|
30
|
+
# resolve ONLY portions of the object which are along our path
|
31
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
32
|
+
Hocon::Impl::ConfigImpl.trace("*** finding '#{path}' in #{obj}")
|
33
|
+
end
|
34
|
+
restriction = context.restrict_to_child
|
35
|
+
partially_resolved = context.restrict(path).resolve(obj, self.class.new(obj))
|
36
|
+
new_context = partially_resolved.context.restrict(restriction)
|
37
|
+
if partially_resolved.value.is_a?(Hocon::Impl::AbstractConfigObject)
|
38
|
+
pair = self.class.find_in_object_impl(partially_resolved.value, path)
|
39
|
+
ResultWithPath.new(Hocon::Impl::ResolveResult.make(new_context, pair.value), pair.path_from_root)
|
40
|
+
else
|
41
|
+
raise ConfigBugOrBrokenError.new("resolved object to non-object " + obj + " to " + partially_resolved)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def lookup_subst(context, subst, prefix_length)
|
46
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
47
|
+
Hocon::Impl::ConfigImpl.trace("searching for #{subst}", context.depth)
|
48
|
+
end
|
49
|
+
|
50
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
51
|
+
Hocon::Impl::ConfigImpl.trace("#{subst} - looking up relative to file it occurred in",
|
52
|
+
context.depth)
|
53
|
+
end
|
54
|
+
# First we look up the full path, which means relative to the
|
55
|
+
# included file if we were not a root file
|
56
|
+
result = find_in_object(@root, context, subst.path)
|
57
|
+
|
58
|
+
if result.result.value == nil
|
59
|
+
# Then we want to check relative to the root file.We don 't
|
60
|
+
# want the prefix we were included at to be used when looking
|
61
|
+
# up env variables either.
|
62
|
+
unprefixed = subst.path.sub_path_to_end(prefix_length)
|
63
|
+
|
64
|
+
if prefix_length > 0
|
65
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
66
|
+
Hocon::Impl::ConfigImpl.trace(
|
67
|
+
unprefixed + " - looking up relative to parent file",
|
68
|
+
result.result.context.depth)
|
69
|
+
end
|
70
|
+
result = find_in_object(@root, result.result.context, unprefixed)
|
71
|
+
end
|
72
|
+
|
73
|
+
if result.result.value == nil && result.result.context.options.use_system_environment
|
74
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
75
|
+
Hocon::Impl::ConfigImpl.trace(
|
76
|
+
"#{unprefixed} - looking up in system environment",
|
77
|
+
result.result.context.depth)
|
78
|
+
end
|
79
|
+
result = find_in_object(Hocon::Impl::ConfigImpl.env_variables_as_config_object, context, unprefixed)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
84
|
+
Hocon::Impl::ConfigImpl.trace(
|
85
|
+
"resolved to #{result}",
|
86
|
+
result.result.context.depth)
|
87
|
+
end
|
88
|
+
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
def push_parent(parent)
|
93
|
+
unless parent
|
94
|
+
raise ConfigBugOrBrokenError.new("can't push null parent")
|
95
|
+
end
|
96
|
+
|
97
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
98
|
+
Hocon::Impl::ConfigImpl.trace("pushing parent #{parent} ==root #{(parent == root)} onto #{self}")
|
99
|
+
end
|
100
|
+
|
101
|
+
if @path_from_root == nil
|
102
|
+
if parent.equal?(@root)
|
103
|
+
return self.class.new(@root, Node.new(parent))
|
104
|
+
else
|
105
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
106
|
+
# this hasDescendant check is super-expensive so it's a
|
107
|
+
# trace message rather than an assertion
|
108
|
+
if @root.has_descendant?(parent)
|
109
|
+
Hocon::Impl::ConfigImpl.trace(
|
110
|
+
"***** BUG ***** tried to push parent #{parent} without having a path to it in #{self}")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
# ignore parents if we aren't proceeding from the
|
114
|
+
# root
|
115
|
+
return self
|
116
|
+
end
|
117
|
+
else
|
118
|
+
parent_parent = @path_from_root.head
|
119
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
120
|
+
# this hasDescendant check is super-expensive so it's a
|
121
|
+
# trace message rather than an assertion
|
122
|
+
if parent_parent != nil && !parent_parent.has_descendant?(parent)
|
123
|
+
Hocon::Impl::ConfigImpl.trace(
|
124
|
+
"***** BUG ***** trying to push non-child of #{parent_parent}, non-child was #{parent}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
self.class.new(@root, @path_from_root.prepend(parent))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def reset_parents
|
133
|
+
if @path_from_root == nil
|
134
|
+
this
|
135
|
+
else
|
136
|
+
self.class.new(@root)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def replace_current_parent(old, replacement)
|
141
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
142
|
+
Hocon::Impl::ConfigImpl.trace("replaceCurrentParent old #{old}@#{old.hash} replacement " +
|
143
|
+
"#{replacement}@#{old.hash} in #{self}")
|
144
|
+
end
|
145
|
+
if old.equal?(replacement)
|
146
|
+
self
|
147
|
+
elsif @path_from_root != nil
|
148
|
+
new_path = self.class.replace(@path_from_root, old, replacement)
|
149
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
150
|
+
Hocon::Impl::ConfigImpl.trace("replaced #{old} with #{replacement} in #{self}")
|
151
|
+
Hocon::Impl::ConfigImpl.trace("path was: #{@path_from_root} is now #{new_path}")
|
152
|
+
end
|
153
|
+
# if we end up nuking the root object itself, we replace it with an
|
154
|
+
# empty root
|
155
|
+
if new_path != nil
|
156
|
+
return self.class.new(new_path.last, new_path)
|
157
|
+
else
|
158
|
+
return self.class.new(Hocon::Impl::SimpleConfigObject.empty)
|
159
|
+
end
|
160
|
+
else
|
161
|
+
if old.equal?(@root)
|
162
|
+
return self.class.new(root_must_be_obj(replacement))
|
163
|
+
else
|
164
|
+
raise ConfigBugOrBrokenError.new("attempt to replace root #{root} with #{replacement}")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# replacement may be null to delete
|
170
|
+
def replace_within_current_parent(old, replacement)
|
171
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
172
|
+
Hocon::Impl::ConfigImpl.trace("replaceWithinCurrentParent old #{old}@#{old.hash}" +
|
173
|
+
" replacement #{replacement}@#{old.hash} in #{self}")
|
174
|
+
end
|
175
|
+
if old.equal?(replacement)
|
176
|
+
self
|
177
|
+
elsif @path_from_root != nil
|
178
|
+
parent = @path_from_root.head
|
179
|
+
new_parent = parent.replace_child(old, replacement)
|
180
|
+
return replace_current_parent(parent, new_parent.is_a?(Hocon::Impl::Container) ? new_parent : nil)
|
181
|
+
else
|
182
|
+
if old.equal?(@root) && replacement.is_a?(Hocon::Impl::Container)
|
183
|
+
return self.class.new(root_must_be_obj(replacement))
|
184
|
+
else
|
185
|
+
raise ConfigBugOrBrokenError.new("replace in parent not possible #{old} with #{replacement}" +
|
186
|
+
" in #{self}")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def to_s
|
192
|
+
"ResolveSource(root=#{@root}, pathFromRoot=#{@path_from_root})"
|
193
|
+
end
|
194
|
+
|
195
|
+
# a persistent list
|
196
|
+
class Node
|
197
|
+
attr_reader :next_node, :value
|
198
|
+
|
199
|
+
def initialize(value, next_node = nil)
|
200
|
+
@value = value
|
201
|
+
@next_node = next_node
|
202
|
+
end
|
203
|
+
|
204
|
+
def prepend(value)
|
205
|
+
Node.new(value, self)
|
206
|
+
end
|
207
|
+
|
208
|
+
def head
|
209
|
+
@value
|
210
|
+
end
|
211
|
+
|
212
|
+
def tail
|
213
|
+
@next_node
|
214
|
+
end
|
215
|
+
|
216
|
+
def last
|
217
|
+
i = self
|
218
|
+
while i.next_node != nil
|
219
|
+
i = i.next_node
|
220
|
+
end
|
221
|
+
i.value
|
222
|
+
end
|
223
|
+
|
224
|
+
def reverse
|
225
|
+
if @next_node == nil
|
226
|
+
self
|
227
|
+
else
|
228
|
+
reversed = Node.new(@value)
|
229
|
+
i = @next_node
|
230
|
+
while i != nil
|
231
|
+
reversed = reversed.prepend(i.value)
|
232
|
+
i = i.next_node
|
233
|
+
end
|
234
|
+
reversed
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def to_s
|
239
|
+
sb = ""
|
240
|
+
sb << "["
|
241
|
+
to_append_value = self.reverse
|
242
|
+
while to_append_value != nil
|
243
|
+
sb << to_append_value.value.to_s
|
244
|
+
if to_append_value.next_node != nil
|
245
|
+
sb << " <= "
|
246
|
+
end
|
247
|
+
to_append_value = to_append_value.next_node
|
248
|
+
end
|
249
|
+
sb << "]"
|
250
|
+
sb
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# value is allowed to be null
|
255
|
+
class ValueWithPath
|
256
|
+
attr_reader :value, :path_from_root
|
257
|
+
|
258
|
+
def initialize(value, path_from_root)
|
259
|
+
@value = value
|
260
|
+
@path_from_root = path_from_root
|
261
|
+
end
|
262
|
+
|
263
|
+
def to_s
|
264
|
+
"ValueWithPath(value=" + @value + ", pathFromRoot=" + @path_from_root + ")"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
class ResultWithPath
|
269
|
+
attr_reader :result, :path_from_root
|
270
|
+
|
271
|
+
def initialize(result, path_from_root)
|
272
|
+
@result = result
|
273
|
+
@path_from_root = path_from_root
|
274
|
+
end
|
275
|
+
|
276
|
+
def to_s
|
277
|
+
"ResultWithPath(result=#{@result}, pathFromRoot=#{@path_from_root})"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
private
|
282
|
+
|
283
|
+
def root_must_be_obj(value)
|
284
|
+
if value.is_a?(Hocon::Impl::AbstractConfigObject)
|
285
|
+
value
|
286
|
+
else
|
287
|
+
Hocon::Impl::SimpleConfigObject.empty
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def self.find_in_object_impl(obj, path, parents = nil)
|
292
|
+
begin
|
293
|
+
# we 'll fail if anything along the path can' t
|
294
|
+
# be looked at without resolving.
|
295
|
+
find_in_object_impl_impl(obj, path, nil)
|
296
|
+
rescue ConfigNotResolvedError => e
|
297
|
+
raise Hocon::Impl::ConfigImpl.improve_not_resolved(path, e)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def self.find_in_object_impl_impl(obj, path, parents)
|
302
|
+
key = path.first
|
303
|
+
remainder = path.remainder
|
304
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
305
|
+
Hocon::Impl::ConfigImpl.trace("*** looking up '#{key}' in #{obj}")
|
306
|
+
end
|
307
|
+
v = obj.attempt_peek_with_partial_resolve(key)
|
308
|
+
new_parents = parents == nil ? Node.new(obj) : parents.prepend(obj)
|
309
|
+
|
310
|
+
if remainder == nil
|
311
|
+
ValueWithPath.new(v, new_parents)
|
312
|
+
else
|
313
|
+
if v.is_a?(Hocon::Impl::AbstractConfigObject)
|
314
|
+
find_in_object_impl_impl(v, remainder, new_parents)
|
315
|
+
else
|
316
|
+
ValueWithPath.new(nil, new_parents)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# returns null if the replacement results in deleting all the nodes.
|
322
|
+
def self.replace(list, old, replacement)
|
323
|
+
child = list.head
|
324
|
+
unless child.equal?(old)
|
325
|
+
raise ConfigBugOrBrokenError.new("Can only replace() the top node we're resolving; had " + child +
|
326
|
+
" on top and tried to replace " + old + " overall list was " + list)
|
327
|
+
end
|
328
|
+
parent = list.tail == nil ? nil : list.tail.head
|
329
|
+
if replacement == nil || !replacement.is_a?(Hocon::Impl::Container)
|
330
|
+
if parent == nil
|
331
|
+
return nil
|
332
|
+
else
|
333
|
+
# we are deleting the child from the stack of containers
|
334
|
+
# because it's either going away or not a container
|
335
|
+
new_parent = parent.replace_child(old, nil)
|
336
|
+
|
337
|
+
return replace(list.tail, parent, new_parent)
|
338
|
+
end
|
339
|
+
else
|
340
|
+
# we replaced the container with another container
|
341
|
+
if parent == nil
|
342
|
+
return Node.new(replacement)
|
343
|
+
else
|
344
|
+
new_parent = parent.replace_child(old, replacement)
|
345
|
+
new_tail = replace(list.tail, parent, new_parent)
|
346
|
+
if new_tail != nil
|
347
|
+
return new_tail.prepend(replacement)
|
348
|
+
else
|
349
|
+
return Node.new(replacement)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|