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