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
@@ -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
|