hocon 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +4 -2
  3. data/lib/hocon.rb +2 -0
  4. data/lib/hocon/config.rb +1010 -0
  5. data/lib/hocon/config_error.rb +32 -2
  6. data/lib/hocon/config_factory.rb +46 -0
  7. data/lib/hocon/config_include_context.rb +49 -0
  8. data/lib/hocon/config_includer_file.rb +27 -0
  9. data/lib/hocon/config_list.rb +49 -0
  10. data/lib/hocon/config_mergeable.rb +74 -0
  11. data/lib/hocon/config_object.rb +144 -1
  12. data/lib/hocon/config_parse_options.rb +33 -9
  13. data/lib/hocon/config_parseable.rb +51 -0
  14. data/lib/hocon/config_render_options.rb +4 -2
  15. data/lib/hocon/config_resolve_options.rb +31 -0
  16. data/lib/hocon/config_syntax.rb +5 -2
  17. data/lib/hocon/config_util.rb +73 -0
  18. data/lib/hocon/config_value.rb +122 -0
  19. data/lib/hocon/config_value_factory.rb +66 -2
  20. data/lib/hocon/config_value_type.rb +5 -2
  21. data/lib/hocon/impl.rb +2 -0
  22. data/lib/hocon/impl/abstract_config_node.rb +29 -0
  23. data/lib/hocon/impl/abstract_config_node_value.rb +11 -0
  24. data/lib/hocon/impl/abstract_config_object.rb +148 -42
  25. data/lib/hocon/impl/abstract_config_value.rb +251 -11
  26. data/lib/hocon/impl/array_iterator.rb +19 -0
  27. data/lib/hocon/impl/config_boolean.rb +7 -1
  28. data/lib/hocon/impl/config_concatenation.rb +177 -28
  29. data/lib/hocon/impl/config_delayed_merge.rb +329 -0
  30. data/lib/hocon/impl/config_delayed_merge_object.rb +274 -0
  31. data/lib/hocon/impl/config_document_parser.rb +647 -0
  32. data/lib/hocon/impl/config_double.rb +44 -0
  33. data/lib/hocon/impl/config_impl.rb +143 -19
  34. data/lib/hocon/impl/config_impl_util.rb +18 -0
  35. data/lib/hocon/impl/config_include_kind.rb +10 -0
  36. data/lib/hocon/impl/config_int.rb +13 -1
  37. data/lib/hocon/impl/config_node_array.rb +11 -0
  38. data/lib/hocon/impl/config_node_comment.rb +19 -0
  39. data/lib/hocon/impl/config_node_complex_value.rb +54 -0
  40. data/lib/hocon/impl/config_node_concatenation.rb +11 -0
  41. data/lib/hocon/impl/config_node_field.rb +81 -0
  42. data/lib/hocon/impl/config_node_include.rb +33 -0
  43. data/lib/hocon/impl/config_node_object.rb +276 -0
  44. data/lib/hocon/impl/config_node_path.rb +48 -0
  45. data/lib/hocon/impl/config_node_root.rb +60 -0
  46. data/lib/hocon/impl/config_node_simple_value.rb +42 -0
  47. data/lib/hocon/impl/config_node_single_token.rb +17 -0
  48. data/lib/hocon/impl/config_null.rb +15 -7
  49. data/lib/hocon/impl/config_number.rb +43 -4
  50. data/lib/hocon/impl/config_parser.rb +403 -0
  51. data/lib/hocon/impl/config_reference.rb +142 -0
  52. data/lib/hocon/impl/config_string.rb +55 -7
  53. data/lib/hocon/impl/container.rb +29 -0
  54. data/lib/hocon/impl/default_transformer.rb +24 -15
  55. data/lib/hocon/impl/from_map_mode.rb +3 -1
  56. data/lib/hocon/impl/full_includer.rb +2 -0
  57. data/lib/hocon/impl/memo_key.rb +42 -0
  58. data/lib/hocon/impl/mergeable_value.rb +8 -0
  59. data/lib/hocon/impl/origin_type.rb +8 -2
  60. data/lib/hocon/impl/parseable.rb +455 -91
  61. data/lib/hocon/impl/path.rb +181 -59
  62. data/lib/hocon/impl/path_builder.rb +24 -3
  63. data/lib/hocon/impl/path_parser.rb +280 -0
  64. data/lib/hocon/impl/replaceable_merge_stack.rb +22 -0
  65. data/lib/hocon/impl/resolve_context.rb +254 -0
  66. data/lib/hocon/impl/resolve_memos.rb +21 -0
  67. data/lib/hocon/impl/resolve_result.rb +39 -0
  68. data/lib/hocon/impl/resolve_source.rb +354 -0
  69. data/lib/hocon/impl/resolve_status.rb +3 -1
  70. data/lib/hocon/impl/simple_config.rb +264 -10
  71. data/lib/hocon/impl/simple_config_document.rb +48 -0
  72. data/lib/hocon/impl/simple_config_list.rb +282 -8
  73. data/lib/hocon/impl/simple_config_object.rb +424 -88
  74. data/lib/hocon/impl/simple_config_origin.rb +263 -71
  75. data/lib/hocon/impl/simple_include_context.rb +31 -1
  76. data/lib/hocon/impl/simple_includer.rb +196 -1
  77. data/lib/hocon/impl/substitution_expression.rb +38 -0
  78. data/lib/hocon/impl/token.rb +17 -4
  79. data/lib/hocon/impl/token_type.rb +6 -2
  80. data/lib/hocon/impl/tokenizer.rb +339 -109
  81. data/lib/hocon/impl/tokens.rb +330 -79
  82. data/lib/hocon/impl/unmergeable.rb +14 -1
  83. data/lib/hocon/impl/unsupported_operation_error.rb +6 -0
  84. data/lib/hocon/impl/url.rb +37 -0
  85. data/lib/hocon/parser.rb +7 -0
  86. data/lib/hocon/parser/config_document.rb +92 -0
  87. data/lib/hocon/parser/config_document_factory.rb +36 -0
  88. data/lib/hocon/parser/config_node.rb +30 -0
  89. metadata +67 -43
  90. data/lib/hocon/impl/config_float.rb +0 -13
  91. data/lib/hocon/impl/parser.rb +0 -977
  92. data/lib/hocon/impl/properties_parser.rb +0 -83
@@ -0,0 +1,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