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