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,142 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon'
|
4
|
+
require 'hocon/impl'
|
5
|
+
require 'hocon/impl/abstract_config_value'
|
6
|
+
require 'hocon/impl/resolve_source'
|
7
|
+
require 'hocon/impl/resolve_result'
|
8
|
+
|
9
|
+
class Hocon::Impl::ConfigReference
|
10
|
+
include Hocon::Impl::Unmergeable
|
11
|
+
include Hocon::Impl::AbstractConfigValue
|
12
|
+
|
13
|
+
NotPossibleToResolve = Hocon::Impl::AbstractConfigValue::NotPossibleToResolve
|
14
|
+
UnresolvedSubstitutionError = Hocon::ConfigError::UnresolvedSubstitutionError
|
15
|
+
|
16
|
+
attr_reader :expr, :prefix_length
|
17
|
+
|
18
|
+
def initialize(origin, expr, prefix_length = 0)
|
19
|
+
super(origin)
|
20
|
+
@expr = expr
|
21
|
+
@prefix_length = prefix_length
|
22
|
+
end
|
23
|
+
|
24
|
+
def unmerged_values
|
25
|
+
[self]
|
26
|
+
end
|
27
|
+
|
28
|
+
# ConfigReference should be a firewall against NotPossibleToResolve going
|
29
|
+
# further up the stack; it should convert everything to ConfigException.
|
30
|
+
# This way it 's impossible for NotPossibleToResolve to "escape" since
|
31
|
+
# any failure to resolve has to start with a ConfigReference.
|
32
|
+
def resolve_substitutions(context, source)
|
33
|
+
new_context = context.add_cycle_marker(self)
|
34
|
+
begin
|
35
|
+
result_with_path = source.lookup_subst(new_context, @expr, @prefix_length)
|
36
|
+
new_context = result_with_path.result.context
|
37
|
+
|
38
|
+
if result_with_path.result.value != nil
|
39
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
40
|
+
Hocon::Impl::ConfigImpl.trace(
|
41
|
+
"recursively resolving #{result_with_path} which was the resolution of #{expr} against #{source}",
|
42
|
+
context.depth)
|
43
|
+
end
|
44
|
+
|
45
|
+
recursive_resolve_source = Hocon::Impl::ResolveSource.new(
|
46
|
+
result_with_path.path_from_root.last, result_with_path.path_from_root)
|
47
|
+
|
48
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
49
|
+
Hocon::Impl::ConfigImpl.trace("will recursively resolve against #{recursive_resolve_source}", context.depth)
|
50
|
+
end
|
51
|
+
|
52
|
+
result = new_context.resolve(result_with_path.result.value,
|
53
|
+
recursive_resolve_source)
|
54
|
+
v = result.value
|
55
|
+
new_context = result.context
|
56
|
+
else
|
57
|
+
v = nil
|
58
|
+
end
|
59
|
+
rescue NotPossibleToResolve => e
|
60
|
+
if Hocon::Impl::ConfigImpl.trace_substitution_enabled
|
61
|
+
Hocon::Impl::ConfigImpl.trace(
|
62
|
+
"not possible to resolve #{expr}, cycle involved: #{e.trace_string}", new_context.depth)
|
63
|
+
end
|
64
|
+
if @expr.optional
|
65
|
+
v = nil
|
66
|
+
else
|
67
|
+
raise UnresolvedSubstitutionError.new(
|
68
|
+
origin,
|
69
|
+
"#{@expr} was part of a cycle of substitutions involving #{e.trace_string}", e)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if v == nil && !@expr.optional
|
74
|
+
if new_context.options.allow_unresolved
|
75
|
+
ResolveResult.make(new_context.remove_cycle_marker(self), self)
|
76
|
+
else
|
77
|
+
raise UnresolvedSubstitutionError.new(origin, @expr.to_s)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
Hocon::Impl::ResolveResult.make(new_context.remove_cycle_marker(self), v)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
def value_type
|
86
|
+
raise not_resolved
|
87
|
+
end
|
88
|
+
|
89
|
+
def unwrapped
|
90
|
+
raise not_resolved
|
91
|
+
end
|
92
|
+
|
93
|
+
def new_copy(new_origin)
|
94
|
+
Hocon::Impl::ConfigReference.new(new_origin, @expr, @prefix_length)
|
95
|
+
end
|
96
|
+
|
97
|
+
def ignores_fallbacks?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
101
|
+
def resolve_status
|
102
|
+
Hocon::Impl::ResolveStatus::UNRESOLVED
|
103
|
+
end
|
104
|
+
|
105
|
+
def relativized(prefix)
|
106
|
+
new_expr = @expr.change_path(@expr.path.prepend(prefix))
|
107
|
+
|
108
|
+
Hocon::Impl::ConfigReference.new(origin, new_expr, @prefix_length + prefix.length)
|
109
|
+
end
|
110
|
+
|
111
|
+
def can_equal(other)
|
112
|
+
other.is_a? Hocon::Impl::ConfigReference
|
113
|
+
end
|
114
|
+
|
115
|
+
def ==(other)
|
116
|
+
# note that "origin" is deliberately NOT part of equality
|
117
|
+
if other.is_a? Hocon::Impl::ConfigReference
|
118
|
+
can_equal(other) && @expr == other.expr
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def hash
|
123
|
+
# note that "origin" is deliberately NOT part of equality
|
124
|
+
@expr.hash
|
125
|
+
end
|
126
|
+
|
127
|
+
def render_value_to_sb(sb, indent, at_root, options)
|
128
|
+
sb << @expr.to_s
|
129
|
+
end
|
130
|
+
|
131
|
+
def expression
|
132
|
+
@expr
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def not_resolved
|
138
|
+
error_message = "need to Config#resolve, see the API docs for Config#resolve; substitution not resolved: #{self}"
|
139
|
+
Hocon::ConfigError::ConfigNotResolvedError.new(error_message, nil)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -1,14 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
2
4
|
require 'hocon/impl/abstract_config_value'
|
3
5
|
require 'hocon/config_value_type'
|
4
6
|
require 'hocon/impl/config_impl_util'
|
5
7
|
|
6
|
-
class Hocon::Impl::ConfigString
|
8
|
+
class Hocon::Impl::ConfigString
|
9
|
+
include Hocon::Impl::AbstractConfigValue
|
10
|
+
|
7
11
|
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
13
|
+
attr_reader :value
|
14
|
+
|
15
|
+
class Quoted < Hocon::Impl::ConfigString
|
16
|
+
def initialize(origin, value)
|
17
|
+
super(origin, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_copy(origin)
|
21
|
+
self.class.new(origin, @value)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# serialization all goes through SerializedConfigValue
|
27
|
+
def write_replace
|
28
|
+
Hocon::Impl::SerializedConfigValue.new(self)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# this is sort of a hack; we want to preserve whether whitespace
|
33
|
+
# was quoted until we process substitutions, so we can ignore
|
34
|
+
# unquoted whitespace when concatenating lists or objects.
|
35
|
+
# We dump this distinction when serializing and deserializing,
|
36
|
+
# but that 's OK because it isn' t in equals/hashCode, and we
|
37
|
+
# don 't allow serializing unresolved objects which is where
|
38
|
+
# quoted-ness matters. If we later make ConfigOrigin point
|
39
|
+
# to the original token range, we could use that to implement
|
40
|
+
# wasQuoted()
|
41
|
+
class Unquoted < Hocon::Impl::ConfigString
|
42
|
+
def initialize(origin, value)
|
43
|
+
super(origin, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def new_copy(origin)
|
47
|
+
self.class.new(origin, @value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_replace
|
51
|
+
Hocon::Impl::SerializedConfigValue.new(self)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def was_quoted?
|
56
|
+
self.is_a?(Quoted)
|
12
57
|
end
|
13
58
|
|
14
59
|
def value_type
|
@@ -31,7 +76,10 @@ class Hocon::Impl::ConfigString < Hocon::Impl::AbstractConfigValue
|
|
31
76
|
end
|
32
77
|
end
|
33
78
|
|
34
|
-
|
35
|
-
|
79
|
+
private
|
80
|
+
|
81
|
+
def initialize(origin, value)
|
82
|
+
super(origin)
|
83
|
+
@value = value
|
36
84
|
end
|
37
|
-
end
|
85
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon/impl'
|
4
|
+
require 'hocon/config_value'
|
5
|
+
require 'hocon/config_error'
|
6
|
+
|
7
|
+
# An AbstractConfigValue which contains other values. Java has no way to
|
8
|
+
# express "this has to be an AbstractConfigValue also" other than making
|
9
|
+
# AbstractConfigValue an interface which would be aggravating. But we can say
|
10
|
+
# we are a ConfigValue.
|
11
|
+
module Hocon::Impl::Container
|
12
|
+
include Hocon::ConfigValue
|
13
|
+
#
|
14
|
+
# Replace a child of this value. CAUTION if replacement is null, delete the
|
15
|
+
# child, which may also delete the parent, or make the parent into a
|
16
|
+
# non-container.
|
17
|
+
#
|
18
|
+
def replace_child(child, replacement)
|
19
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Container` must implement `replace_child` (#{self.class})"
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Super-expensive full traversal to see if descendant is anywhere
|
24
|
+
# underneath this container.
|
25
|
+
#
|
26
|
+
def has_descendant?(descendant)
|
27
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Container` must implement `has_descendant?` (#{self.class})"
|
28
|
+
end
|
29
|
+
end
|
@@ -1,15 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
4
|
+
require 'hocon/impl/config_string'
|
2
5
|
require 'hocon/config_value_type'
|
6
|
+
require 'hocon/impl/config_boolean'
|
3
7
|
|
4
8
|
class Hocon::Impl::DefaultTransformer
|
5
9
|
|
6
10
|
ConfigValueType = Hocon::ConfigValueType
|
11
|
+
ConfigString = Hocon::Impl::ConfigString
|
12
|
+
ConfigBoolean = Hocon::Impl::ConfigBoolean
|
7
13
|
|
8
14
|
def self.transform(value, requested)
|
9
|
-
if value.
|
15
|
+
if value.value_type == ConfigValueType::STRING
|
10
16
|
s = value.unwrapped
|
11
17
|
case requested
|
12
|
-
when NUMBER
|
18
|
+
when ConfigValueType::NUMBER
|
13
19
|
begin
|
14
20
|
v = Integer(s)
|
15
21
|
return ConfigInt.new(value.origin, v, s)
|
@@ -22,21 +28,21 @@ class Hocon::Impl::DefaultTransformer
|
|
22
28
|
rescue ArgumentError
|
23
29
|
# oh well.
|
24
30
|
end
|
25
|
-
when NULL
|
31
|
+
when ConfigValueType::NULL
|
26
32
|
if s == "null"
|
27
33
|
return ConfigNull.new(value.origin)
|
28
34
|
end
|
29
|
-
when BOOLEAN
|
35
|
+
when ConfigValueType::BOOLEAN
|
30
36
|
if s == "true" || s == "yes" || s == "on"
|
31
37
|
return ConfigBoolean.new(value.origin, true)
|
32
38
|
elsif s == "false" || s == "no" || s == "off"
|
33
39
|
return ConfigBoolean.new(value.origin, false)
|
34
40
|
end
|
35
|
-
when LIST
|
41
|
+
when ConfigValueType::LIST
|
36
42
|
# can't go STRING to LIST automatically
|
37
|
-
when OBJECT
|
43
|
+
when ConfigValueType::OBJECT
|
38
44
|
# can't go STRING to OBJECT automatically
|
39
|
-
when STRING
|
45
|
+
when ConfigValueType::STRING
|
40
46
|
# no-op STRING to STRING
|
41
47
|
end
|
42
48
|
elsif requested == ConfigValueType::STRING
|
@@ -44,17 +50,20 @@ class Hocon::Impl::DefaultTransformer
|
|
44
50
|
# get a missing-value error if you tried to get a null value
|
45
51
|
# as a string.
|
46
52
|
case value.value_type
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
53
|
+
# Ruby note: can't fall through in ruby. In the java code, NUMBER
|
54
|
+
# just rolls over to the BOOLEAN case
|
55
|
+
when ConfigValueType::NUMBER
|
56
|
+
return ConfigString::Quoted.new(value.origin, value.transform_to_string)
|
57
|
+
when ConfigValueType::BOOLEAN
|
58
|
+
return ConfigString::Quoted.new(value.origin, value.transform_to_string)
|
59
|
+
when ConfigValueType::NULL
|
51
60
|
# want to be sure this throws instead of returning "null" as a
|
52
61
|
# string
|
53
|
-
when OBJECT
|
62
|
+
when ConfigValueType::OBJECT
|
54
63
|
# no OBJECT to STRING automatically
|
55
|
-
when LIST
|
64
|
+
when ConfigValueType::LIST
|
56
65
|
# no LIST to STRING automatically
|
57
|
-
when STRING
|
66
|
+
when ConfigValueType::STRING
|
58
67
|
# no-op STRING to STRING
|
59
68
|
end
|
60
69
|
elsif requested == ConfigValueType::LIST && value.value_type == ConfigValueType::OBJECT
|
@@ -94,4 +103,4 @@ class Hocon::Impl::DefaultTransformer
|
|
94
103
|
end
|
95
104
|
value
|
96
105
|
end
|
97
|
-
end
|
106
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
2
4
|
require 'hocon/config_error'
|
3
5
|
|
@@ -10,7 +12,7 @@ module Hocon::Impl::FromMapMode
|
|
10
12
|
case from_map_mode
|
11
13
|
when KEYS_ARE_PATHS then "KEYS_ARE_PATHS"
|
12
14
|
when KEYS_ARE_KEYS then "KEYS_ARE_KEYS"
|
13
|
-
else raise ConfigBugOrBrokenError.new("Unrecognized FromMapMode #{from_map_mode}"
|
15
|
+
else raise ConfigBugOrBrokenError.new("Unrecognized FromMapMode #{from_map_mode}")
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon'
|
4
|
+
require 'hocon/impl'
|
5
|
+
|
6
|
+
class Hocon::Impl::MemoKey
|
7
|
+
|
8
|
+
def initialize(value, restrict_to_child_or_nil)
|
9
|
+
@value = value
|
10
|
+
@restrict_to_child_or_nil = restrict_to_child_or_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash
|
14
|
+
h = @value.hash
|
15
|
+
if @restrict_to_child_or_nil != nil
|
16
|
+
h + 41 * (41 + @restrict_to_child_or_nil.hash)
|
17
|
+
else
|
18
|
+
h
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def ==(other)
|
23
|
+
if other.is_a?(self.class)
|
24
|
+
o = other
|
25
|
+
if !o.value.equal?(@value)
|
26
|
+
return false
|
27
|
+
elsif o.restrict_to_child_or_nil.equals(@restrict_to_child_or_nil)
|
28
|
+
return true
|
29
|
+
elsif o.restrict_to_child_or_nil == nil || @restrict_to_child_or_nil == nil
|
30
|
+
return false
|
31
|
+
else
|
32
|
+
return o.restrict_to_child_or_nil == @restrict_to_child_or_nil
|
33
|
+
end
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"MemoKey(#{@value}@#{@value.hash},#{@restrict_to_child_or_nil})"
|
41
|
+
end
|
42
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
2
4
|
|
3
5
|
module Hocon::Impl::OriginType
|
@@ -5,5 +7,9 @@ module Hocon::Impl::OriginType
|
|
5
7
|
GENERIC = 0
|
6
8
|
FILE = 1
|
7
9
|
#URL = 2
|
8
|
-
#
|
9
|
-
|
10
|
+
# We don't actually support loading from the classpath / loadpath, which is
|
11
|
+
# what 'RESOURCE' is about in the upstream library. However, some code paths
|
12
|
+
# still flow through our simplistic implementation of `ParseableResource`, so
|
13
|
+
# we need this constant.
|
14
|
+
RESOURCE = 3
|
15
|
+
end
|
data/lib/hocon/impl/parseable.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'stringio'
|
4
|
+
require 'pathname'
|
2
5
|
require 'hocon/impl'
|
3
6
|
require 'hocon/config_error'
|
4
7
|
require 'hocon/config_syntax'
|
@@ -7,78 +10,108 @@ require 'hocon/impl/simple_include_context'
|
|
7
10
|
require 'hocon/impl/simple_config_object'
|
8
11
|
require 'hocon/impl/simple_config_origin'
|
9
12
|
require 'hocon/impl/tokenizer'
|
10
|
-
require 'hocon/impl/
|
13
|
+
require 'hocon/impl/config_parser'
|
14
|
+
require 'hocon/config_parseable'
|
15
|
+
require 'hocon/impl/config_document_parser'
|
16
|
+
require 'hocon/impl/simple_config_document'
|
11
17
|
|
18
|
+
#
|
19
|
+
# Internal implementation detail, not ABI stable, do not touch.
|
20
|
+
# For use only by the {@link com.typesafe.config} package.
|
21
|
+
# The point of this class is to avoid "propagating" each
|
22
|
+
# overload on "thing which can be parsed" through multiple
|
23
|
+
# interfaces. Most interfaces can have just one overload that
|
24
|
+
# takes a Parseable. Also it's used as an abstract "resource
|
25
|
+
# handle" in the ConfigIncluder interface.
|
26
|
+
#
|
12
27
|
class Hocon::Impl::Parseable
|
13
|
-
|
14
|
-
def initialize(file_path, options)
|
15
|
-
@input = file_path
|
16
|
-
post_construct(options)
|
17
|
-
end
|
28
|
+
include Hocon::ConfigParseable
|
18
29
|
|
19
|
-
|
20
|
-
|
30
|
+
# Internal implementation detail, not ABI stable, do not touch
|
31
|
+
module Relativizer
|
32
|
+
def relative_to(filename)
|
33
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Relativizer` must implement `relative_to` (#{self.class})"
|
21
34
|
end
|
35
|
+
end
|
22
36
|
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
# Changed this to a class variable because the upstream library seems to use it
|
38
|
+
# as a global way of keeping track of how many files have been included, to
|
39
|
+
# avoid cycles
|
40
|
+
@@parse_stack= []
|
26
41
|
|
27
|
-
|
28
|
-
|
29
|
-
|
42
|
+
MAX_INCLUDE_DEPTH = 50
|
43
|
+
|
44
|
+
def initialize
|
30
45
|
|
31
|
-
def open
|
32
|
-
if block_given?
|
33
|
-
File.open(@input) do |f|
|
34
|
-
yield f
|
35
|
-
end
|
36
|
-
else
|
37
|
-
File.open(@input)
|
38
|
-
end
|
39
|
-
end
|
40
46
|
end
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
48
|
+
def fixup_options(base_options)
|
49
|
+
syntax = base_options.syntax
|
50
|
+
if !syntax
|
51
|
+
syntax = guess_syntax
|
46
52
|
end
|
47
|
-
|
48
|
-
|
49
|
-
Hocon::Impl::SimpleConfigOrigin.new_simple("String")
|
53
|
+
if !syntax
|
54
|
+
syntax = Hocon::ConfigSyntax::CONF
|
50
55
|
end
|
56
|
+
modified = base_options.set_syntax(syntax)
|
51
57
|
|
52
|
-
|
53
|
-
|
54
|
-
|
58
|
+
# make sure the app-provided includer falls back to default
|
59
|
+
modified = modified.append_includer(Hocon::Impl::ConfigImpl.default_includer)
|
60
|
+
# make sure the app-provided includer is complete
|
61
|
+
modified = modified.set_includer(Hocon::Impl::SimpleIncluder.make_full(modified.includer))
|
55
62
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
modified
|
64
|
+
end
|
65
|
+
|
66
|
+
def post_construct(base_options)
|
67
|
+
@initial_options = fixup_options(base_options)
|
68
|
+
@include_context = Hocon::Impl::SimpleIncludeContext.new(self)
|
69
|
+
if @initial_options.origin_description
|
70
|
+
@initial_origin = Hocon::Impl::SimpleConfigOrigin.new_simple(@initial_options.origin_description)
|
71
|
+
else
|
72
|
+
@initial_origin = create_origin
|
64
73
|
end
|
74
|
+
end
|
65
75
|
|
76
|
+
# the general idea is that any work should be in here, not in the
|
77
|
+
# constructor, so that exceptions are thrown from the public parse()
|
78
|
+
# function and not from the creation of the Parseable.
|
79
|
+
# Essentially this is a lazy field. The parser should close the
|
80
|
+
# reader when it's done with it.
|
81
|
+
#{//}# ALSO, IMPORTANT: if the file or URL is not found, this must throw.
|
82
|
+
#{//}# to support the "allow missing" feature.
|
83
|
+
def custom_reader
|
84
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `custom_reader` (#{self.class})"
|
66
85
|
end
|
67
86
|
|
68
|
-
def
|
69
|
-
|
87
|
+
def reader(options)
|
88
|
+
custom_reader
|
70
89
|
end
|
71
90
|
|
72
|
-
def self.
|
73
|
-
|
91
|
+
def self.trace(message)
|
92
|
+
if Hocon::Impl::ConfigImpl.trace_loads_enabled
|
93
|
+
Hocon::Impl::ConfigImpl.trace(message)
|
94
|
+
end
|
74
95
|
end
|
75
96
|
|
76
97
|
def guess_syntax
|
77
98
|
nil
|
78
99
|
end
|
79
100
|
|
80
|
-
def
|
81
|
-
|
101
|
+
def content_type
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def relative_to(filename)
|
106
|
+
# fall back to classpath; we treat the "filename" as absolute
|
107
|
+
# (don't add a package name in front),
|
108
|
+
# if it starts with "/" then remove the "/", for consistency
|
109
|
+
# with ParseableResources.relativeTo
|
110
|
+
resource = filename
|
111
|
+
if filename.start_with?("/")
|
112
|
+
resource = filename.slice(1)
|
113
|
+
end
|
114
|
+
self.class.new_resources(resource, options.set_origin_description(nil))
|
82
115
|
end
|
83
116
|
|
84
117
|
def include_context
|
@@ -89,17 +122,40 @@ class Hocon::Impl::Parseable
|
|
89
122
|
if value.is_a? Hocon::Impl::AbstractConfigObject
|
90
123
|
value
|
91
124
|
else
|
92
|
-
raise Hocon::ConfigError::ConfigWrongTypeError.
|
125
|
+
raise Hocon::ConfigError::ConfigWrongTypeError.with_expected_actual(value.origin,
|
126
|
+
"",
|
93
127
|
"object at file root",
|
94
128
|
value.value_type.name)
|
95
129
|
end
|
96
130
|
end
|
97
131
|
|
98
|
-
def parse
|
99
|
-
|
132
|
+
def parse(base_options = nil)
|
133
|
+
if (base_options.nil?)
|
134
|
+
base_options = options
|
135
|
+
end
|
136
|
+
stack = @@parse_stack
|
137
|
+
if stack.length >= MAX_INCLUDE_DEPTH
|
138
|
+
raise Hocon::ConfigError::ConfigParseError.new(@initial_origin,
|
139
|
+
"include statements nested more than #{MAX_INCLUDE_DEPTH} times, " +
|
140
|
+
"you probably have a cycle in your includes. Trace: #{stack}",
|
141
|
+
nil)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Push into beginning of stack
|
145
|
+
stack.unshift(self)
|
146
|
+
begin
|
147
|
+
self.class.force_parsed_to_object(parse_value(base_options))
|
148
|
+
ensure
|
149
|
+
# Pop from beginning of stack
|
150
|
+
stack.shift
|
151
|
+
end
|
100
152
|
end
|
101
153
|
|
102
|
-
def parse_value(base_options)
|
154
|
+
def parse_value(base_options = nil)
|
155
|
+
if base_options.nil?
|
156
|
+
base_options = options
|
157
|
+
end
|
158
|
+
|
103
159
|
# note that we are NOT using our "initialOptions",
|
104
160
|
# but using the ones from the passed-in options. The idea is that
|
105
161
|
# callers can get our original options and then parse with different
|
@@ -116,55 +172,49 @@ class Hocon::Impl::Parseable
|
|
116
172
|
parse_value_from_origin(origin, options)
|
117
173
|
end
|
118
174
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
Hocon::ConfigSyntax::JSON
|
126
|
-
when ".conf"
|
127
|
-
Hocon::ConfigSyntax::CONF
|
128
|
-
when ".properties"
|
129
|
-
Hocon::ConfigSyntax::PROPERTIES
|
175
|
+
def parse_value_from_origin(origin, final_options)
|
176
|
+
begin
|
177
|
+
raw_parse_value(origin, final_options)
|
178
|
+
rescue IOError => e
|
179
|
+
if final_options.allow_missing?
|
180
|
+
Hocon::Impl::SimpleConfigObject.empty_missing(origin)
|
130
181
|
else
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
def post_construct(base_options)
|
136
|
-
@initial_options = fixup_options(base_options)
|
137
|
-
@include_context = Hocon::Impl::SimpleIncludeContext.new(self)
|
138
|
-
if @initial_options.origin_description
|
139
|
-
@initial_origin = SimpleConfigOrigin.new_simple(@initial_options.origin_description)
|
140
|
-
else
|
141
|
-
@initial_origin = create_origin
|
182
|
+
self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}")
|
183
|
+
raise Hocon::ConfigError::ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e)
|
184
|
+
end
|
142
185
|
end
|
143
186
|
end
|
144
187
|
|
145
|
-
def
|
146
|
-
|
147
|
-
|
148
|
-
syntax = guess_syntax
|
149
|
-
end
|
150
|
-
if !syntax
|
151
|
-
syntax = Hocon::ConfigSyntax::CONF
|
188
|
+
def parse_document(base_options = nil)
|
189
|
+
if base_options.nil?
|
190
|
+
base_options = options
|
152
191
|
end
|
153
192
|
|
154
|
-
|
155
|
-
|
156
|
-
|
193
|
+
# note that we are NOT using our "initialOptions",
|
194
|
+
# but using the ones from the passed-in options. The idea is that
|
195
|
+
# callers can get our original options and then parse with different
|
196
|
+
# ones if they want.
|
197
|
+
options = fixup_options(base_options)
|
157
198
|
|
158
|
-
|
199
|
+
# passed-in option can override origin
|
200
|
+
origin = nil
|
201
|
+
if ! options.origin_description.nil?
|
202
|
+
origin = Hocon::Impl::SimpleConfigOrigin.new_simple(options.origin_description)
|
203
|
+
else
|
204
|
+
origin = @initial_origin
|
205
|
+
end
|
206
|
+
parse_document_from_origin(origin, options)
|
159
207
|
end
|
160
208
|
|
161
|
-
def
|
209
|
+
def parse_document_from_origin(origin, final_options)
|
162
210
|
begin
|
163
|
-
|
211
|
+
raw_parse_document(origin, final_options)
|
164
212
|
rescue IOError => e
|
165
213
|
if final_options.allow_missing?
|
166
|
-
Hocon::Impl::
|
214
|
+
Hocon::Impl::SimpleConfigDocument.new(
|
215
|
+
Hocon::Impl::ConfigNodeObject.new([]), final_options)
|
167
216
|
else
|
217
|
+
self.class.trace("exception loading #{origin.description}: #{e.class}: #{e.message}")
|
168
218
|
raise ConfigIOError.new(origin, "#{e.class.name}: #{e.message}", e)
|
169
219
|
end
|
170
220
|
end
|
@@ -173,16 +223,330 @@ class Hocon::Impl::Parseable
|
|
173
223
|
# this is parseValue without post-processing the IOException or handling
|
174
224
|
# options.getAllowMissing()
|
175
225
|
def raw_parse_value(origin, final_options)
|
176
|
-
|
177
|
-
|
226
|
+
reader = reader(final_options)
|
227
|
+
|
228
|
+
# after reader() we will have loaded the Content-Type
|
229
|
+
content_type = content_type()
|
230
|
+
|
231
|
+
options_with_content_type = nil
|
232
|
+
if !(content_type.nil?)
|
233
|
+
if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?)
|
234
|
+
self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}")
|
235
|
+
end
|
236
|
+
options_with_content_type = final_options.set_syntax(content_type)
|
237
|
+
else
|
238
|
+
options_with_content_type = final_options
|
239
|
+
end
|
178
240
|
|
179
241
|
reader.open { |io|
|
180
|
-
raw_parse_value_from_io(io, origin,
|
242
|
+
raw_parse_value_from_io(io, origin, options_with_content_type)
|
181
243
|
}
|
182
244
|
end
|
183
245
|
|
184
246
|
def raw_parse_value_from_io(io, origin, final_options)
|
185
247
|
tokens = Hocon::Impl::Tokenizer.tokenize(origin, io, final_options.syntax)
|
186
|
-
Hocon::Impl::
|
248
|
+
document = Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options)
|
249
|
+
Hocon::Impl::ConfigParser.parse(document, origin, final_options, include_context)
|
250
|
+
end
|
251
|
+
|
252
|
+
def raw_parse_document(origin, final_options)
|
253
|
+
reader = reader(final_options)
|
254
|
+
content_type = content_type()
|
255
|
+
|
256
|
+
options_with_content_type = nil
|
257
|
+
if !(content_type.nil?)
|
258
|
+
if Hocon::Impl::ConfigImpl.trace_loads_enabled && (! final_options.get_syntax.nil?)
|
259
|
+
self.class.trace("Overriding syntax #{final_options.get_syntax} with Content-Type which specified #{content-type}")
|
260
|
+
end
|
261
|
+
options_with_content_type = final_options.set_syntax(content_type)
|
262
|
+
else
|
263
|
+
options_with_content_type = final_options
|
264
|
+
end
|
265
|
+
|
266
|
+
reader.open { |io|
|
267
|
+
raw_parse_document_from_io(io, origin, options_with_content_type)
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
def raw_parse_document_from_io(reader, origin, final_options)
|
272
|
+
tokens = Hocon::Impl::Tokenizer.tokenize(origin, reader, final_options.syntax)
|
273
|
+
Hocon::Impl::SimpleConfigDocument.new(
|
274
|
+
Hocon::Impl::ConfigDocumentParser.parse(tokens, origin, final_options),
|
275
|
+
final_options)
|
276
|
+
end
|
277
|
+
|
278
|
+
def parse_config_document
|
279
|
+
parse_document(options)
|
280
|
+
end
|
281
|
+
|
282
|
+
def origin
|
283
|
+
@initial_origin
|
284
|
+
end
|
285
|
+
|
286
|
+
def create_origin
|
287
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "subclasses of `Parseable` must implement `create_origin` (#{self.class})"
|
288
|
+
end
|
289
|
+
|
290
|
+
def options
|
291
|
+
@initial_options
|
292
|
+
end
|
293
|
+
|
294
|
+
def to_s
|
295
|
+
self.class.name.split('::').last
|
296
|
+
end
|
297
|
+
|
298
|
+
def self.syntax_from_extension(name)
|
299
|
+
if name.end_with?(".json")
|
300
|
+
Hocon::ConfigSyntax::JSON
|
301
|
+
elsif name.end_with?(".conf")
|
302
|
+
Hocon::ConfigSyntax::CONF
|
303
|
+
else
|
304
|
+
# Skipping PROPERTIES because we can't really support that in ruby
|
305
|
+
nil
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# NOTE: skipping `readerFromStream` and `doNotClose` because they don't seem relevant in Ruby
|
310
|
+
|
311
|
+
# NOTE: skipping `relativeTo(URL, String)` because we're not supporting URLs for now
|
312
|
+
def self.relative_to(file, filename)
|
313
|
+
child = Pathname.new(filename)
|
314
|
+
file = Pathname.new(file)
|
315
|
+
|
316
|
+
if child.absolute?
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
|
320
|
+
parent = file.parent
|
321
|
+
|
322
|
+
if parent.nil?
|
323
|
+
nil
|
324
|
+
else
|
325
|
+
File.join(parent, filename)
|
326
|
+
end
|
187
327
|
end
|
328
|
+
|
329
|
+
# this is a parseable that doesn't exist and just throws when you try to parse it
|
330
|
+
class ParseableNotFound < Hocon::Impl::Parseable
|
331
|
+
def initialize(what, message, options)
|
332
|
+
super()
|
333
|
+
@what = what
|
334
|
+
@message = message
|
335
|
+
post_construct(options)
|
336
|
+
end
|
337
|
+
|
338
|
+
def custom_reader
|
339
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, @message
|
340
|
+
end
|
341
|
+
|
342
|
+
def create_origin
|
343
|
+
Hocon::Impl::SimpleConfigOrigin.new_simple(@what)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.new_not_found(what_not_found, message, options)
|
348
|
+
ParseableNotFound.new(what_not_found, message, options)
|
349
|
+
end
|
350
|
+
|
351
|
+
# NOTE: skipping `ParseableReader` until we know we need it (probably should
|
352
|
+
# have done that with `ParseableNotFound`)
|
353
|
+
|
354
|
+
class ParseableString < Hocon::Impl::Parseable
|
355
|
+
def initialize(string, options)
|
356
|
+
super()
|
357
|
+
@input = string
|
358
|
+
post_construct(options)
|
359
|
+
end
|
360
|
+
|
361
|
+
def custom_reader
|
362
|
+
if Hocon::Impl::ConfigImpl.trace_loads_enabled
|
363
|
+
self.class.trace("Loading config from a String: #{@input}")
|
364
|
+
end
|
365
|
+
# we return self here, which will cause `open` to be called on us, so
|
366
|
+
# we can provide an implementation of that.
|
367
|
+
self
|
368
|
+
end
|
369
|
+
|
370
|
+
def open
|
371
|
+
if block_given?
|
372
|
+
StringIO.open(@input) do |f|
|
373
|
+
yield f
|
374
|
+
end
|
375
|
+
else
|
376
|
+
StringIO.open(@input)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def create_origin
|
381
|
+
Hocon::Impl::SimpleConfigOrigin.new_simple("String")
|
382
|
+
end
|
383
|
+
|
384
|
+
def to_s
|
385
|
+
"#{self.class.name.split('::').last} (#{@input})"
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def self.new_string(string, options)
|
390
|
+
ParseableString.new(string, options)
|
391
|
+
end
|
392
|
+
|
393
|
+
# NOTE: Skipping `ParseableURL` for now as we probably won't support this right away
|
394
|
+
|
395
|
+
class ParseableFile < Hocon::Impl::Parseable
|
396
|
+
def initialize(input, options)
|
397
|
+
super()
|
398
|
+
@input = input
|
399
|
+
post_construct(options)
|
400
|
+
end
|
401
|
+
|
402
|
+
def custom_reader
|
403
|
+
if Hocon::Impl::ConfigImpl.trace_loads_enabled
|
404
|
+
self.class.trace("Loading config from a String: #{@input}")
|
405
|
+
end
|
406
|
+
# we return self here, which will cause `open` to be called on us, so
|
407
|
+
# we can provide an implementation of that.
|
408
|
+
self
|
409
|
+
end
|
410
|
+
|
411
|
+
def open
|
412
|
+
begin
|
413
|
+
if block_given?
|
414
|
+
File.open(@input) do |f|
|
415
|
+
yield f
|
416
|
+
end
|
417
|
+
else
|
418
|
+
File.open(@input)
|
419
|
+
end
|
420
|
+
rescue Errno::ENOENT
|
421
|
+
if @initial_options.allow_missing?
|
422
|
+
return Hocon::Impl::SimpleConfigObject.empty
|
423
|
+
end
|
424
|
+
|
425
|
+
raise Hocon::ConfigError::ConfigIOError.new(nil, "File not found. No file called #{@input}")
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def guess_syntax
|
430
|
+
Hocon::Impl::Parseable.syntax_from_extension(File.basename(@input))
|
431
|
+
end
|
432
|
+
|
433
|
+
def relative_to(filename)
|
434
|
+
sibling = nil
|
435
|
+
if Pathname.new(filename).absolute?
|
436
|
+
sibling = File.new(filename)
|
437
|
+
else
|
438
|
+
# this may return nil
|
439
|
+
sibling = Hocon::Impl::Parseable.relative_to(@input, filename)
|
440
|
+
end
|
441
|
+
if sibling.nil?
|
442
|
+
nil
|
443
|
+
elsif File.exists?(sibling)
|
444
|
+
self.class.trace("#{sibling} exists, so loading it as a file")
|
445
|
+
Hocon::Impl::Parseable.new_file(sibling, options.set_origin_description(nil))
|
446
|
+
else
|
447
|
+
self.class.trace("#{sibling} does not exist, so trying it as a resource")
|
448
|
+
super(filename)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def create_origin
|
453
|
+
Hocon::Impl::SimpleConfigOrigin.new_file(@input)
|
454
|
+
end
|
455
|
+
|
456
|
+
def to_s
|
457
|
+
"#{self.class.name.split('::').last} (#{@input})"
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
|
462
|
+
|
463
|
+
def self.new_file(file_path, options)
|
464
|
+
ParseableFile.new(file_path, options)
|
465
|
+
end
|
466
|
+
|
467
|
+
# NOTE: skipping `ParseableResourceURL`, we probably won't support that
|
468
|
+
|
469
|
+
# NOTE: this is not a faithful port of the `ParseableResources` class from the
|
470
|
+
# upstream, because at least for now we're not going to try to do anything
|
471
|
+
# crazy like look for files on the ruby load path. However, there is a decent
|
472
|
+
# chunk of logic elsewhere in the codebase that is written with the assumption
|
473
|
+
# that this class will provide the 'last resort' attempt to find a config file
|
474
|
+
# before giving up, so we're basically port just enough to have it provide
|
475
|
+
# that last resort behavior
|
476
|
+
class ParseableResources < Hocon::Impl::Parseable
|
477
|
+
include Relativizer
|
478
|
+
|
479
|
+
def initialize(resource, options)
|
480
|
+
super()
|
481
|
+
@resource = resource
|
482
|
+
post_construct(options)
|
483
|
+
end
|
484
|
+
|
485
|
+
def reader
|
486
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "reader() should not be called on resources"
|
487
|
+
end
|
488
|
+
|
489
|
+
def raw_parse_value(origin, final_options)
|
490
|
+
# this is where the upstream code would go out and look for a file on the
|
491
|
+
# classpath. We're not going to do that, and instead we're just going to
|
492
|
+
# raise the same exception that the upstream code would raise if it failed
|
493
|
+
# to find the file.
|
494
|
+
raise IOError, "resource not found: #{@resource}"
|
495
|
+
end
|
496
|
+
|
497
|
+
def guess_syntax
|
498
|
+
Hocon::Impl::Parseable.syntax_from_extension(@resource)
|
499
|
+
end
|
500
|
+
|
501
|
+
def self.parent(resource)
|
502
|
+
# the "resource" is not supposed to begin with a "/"
|
503
|
+
# because it's supposed to be the raw resource
|
504
|
+
# (ClassLoader#getResource), not the
|
505
|
+
# resource "syntax" (Class#getResource)
|
506
|
+
i = resource.rindex("/")
|
507
|
+
if i < 0
|
508
|
+
nil
|
509
|
+
else
|
510
|
+
resource.slice(0..i)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def relative_to(sibling)
|
515
|
+
if sibling.start_with?("/")
|
516
|
+
# if it starts with "/" then don't make it relative to the
|
517
|
+
# including resource
|
518
|
+
Hocon::Impl::Parseable.new_resources(sibling.slice(1), options.set_origin_description(nil))
|
519
|
+
else
|
520
|
+
# here we want to build a new resource name and let
|
521
|
+
# the class loader have it, rather than getting the
|
522
|
+
# url with getResource() and relativizing to that url.
|
523
|
+
# This is needed in case the class loader is going to
|
524
|
+
# search a classpath.
|
525
|
+
parent = self.class.parent(@resource)
|
526
|
+
if parent.nil?
|
527
|
+
Hocon::Impl::Parseable.new_resources(sibling, options.set_origin_description(nil))
|
528
|
+
else
|
529
|
+
Hocon::Impl::Parseable.new_resources("#{parent}/sibling", options.set_origin_description(nil))
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
def create_origin
|
535
|
+
Hocon::Impl::SimpleConfigOrigin.new_resource(@resource)
|
536
|
+
end
|
537
|
+
|
538
|
+
def to_s
|
539
|
+
"#{self.class.name.split('::').last}(#{@resource})"
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
def self.new_resources(resource, options)
|
544
|
+
ParseableResources.new(resource, options)
|
545
|
+
end
|
546
|
+
|
547
|
+
|
548
|
+
|
549
|
+
|
550
|
+
# NOTE: skipping `ParseableProperties`, we probably won't support that
|
551
|
+
|
188
552
|
end
|