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
data/lib/hocon/impl/path.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
2
4
|
require 'hocon/impl/path_builder'
|
3
5
|
require 'hocon/config_error'
|
@@ -8,37 +10,85 @@ class Hocon::Impl::Path
|
|
8
10
|
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
|
9
11
|
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
def self.has_funky_chars?(s)
|
14
|
-
length = s.length
|
15
|
-
if length == 0
|
16
|
-
return false
|
17
|
-
end
|
13
|
+
def initialize(first, remainder)
|
14
|
+
# first: String, remainder: Path
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
@first = first
|
17
|
+
@remainder = remainder
|
18
|
+
end
|
19
|
+
attr_reader :first, :remainder
|
20
|
+
|
21
|
+
def self.from_string_list(elements)
|
22
|
+
# This method was translated from the Path constructor in the
|
23
|
+
# Java hocon library that has this signature:
|
24
|
+
# Path(String... elements)
|
25
|
+
#
|
26
|
+
# It figures out what @first and @remainder should be, then
|
27
|
+
# pass those to the ruby constructor
|
28
|
+
if elements.length == 0
|
29
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError.new("empty path")
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
new_first = elements.first
|
33
|
+
|
34
|
+
if elements.length > 1
|
35
|
+
pb = Hocon::Impl::PathBuilder.new
|
36
|
+
|
37
|
+
# Skip first element
|
38
|
+
elements.drop(1).each do |element|
|
39
|
+
pb.append_key(element)
|
31
40
|
end
|
41
|
+
|
42
|
+
new_remainder = pb.result
|
43
|
+
else
|
44
|
+
new_remainder = nil
|
32
45
|
end
|
33
46
|
|
34
|
-
|
47
|
+
self.new(new_first, new_remainder)
|
35
48
|
end
|
36
49
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
50
|
+
def self.from_path_list(path_list)
|
51
|
+
# This method was translated from the Path constructors in the
|
52
|
+
# Java hocon library that take in a list of Paths
|
53
|
+
#
|
54
|
+
# It just passes an iterator to self.from_path_iterator, which
|
55
|
+
# will return a new Path object
|
56
|
+
from_path_iterator(path_list.each)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.from_path_iterator(path_iterator)
|
60
|
+
# This method was translated from the Path constructors in the
|
61
|
+
# Java hocon library that takes in an iterator of Paths
|
62
|
+
#
|
63
|
+
# It figures out what @first and @remainder should be, then
|
64
|
+
# pass those to the ruby constructor
|
65
|
+
|
66
|
+
# Try to get first path from iterator
|
67
|
+
# Ruby iterators have no .hasNext() method like java
|
68
|
+
# So we try to catch the StopIteration exception
|
69
|
+
begin
|
70
|
+
first_path = path_iterator.next
|
71
|
+
rescue StopIteration
|
72
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError("empty path")
|
73
|
+
end
|
74
|
+
|
75
|
+
new_first = first_path.first
|
76
|
+
|
77
|
+
pb = Hocon::Impl::PathBuilder.new
|
78
|
+
|
79
|
+
unless first_path.remainder.nil?
|
80
|
+
pb.append_path(first_path.remainder)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Skip first path
|
84
|
+
path_iterator.drop(1).each do |path|
|
85
|
+
pb.append_path(path)
|
86
|
+
end
|
87
|
+
|
88
|
+
new_remainder = pb.result
|
89
|
+
|
90
|
+
self.new(new_first, new_remainder)
|
40
91
|
end
|
41
|
-
attr_reader :first, :remainder
|
42
92
|
|
43
93
|
def first
|
44
94
|
@first
|
@@ -64,45 +114,50 @@ class Hocon::Impl::Path
|
|
64
114
|
|
65
115
|
def last
|
66
116
|
p = self
|
67
|
-
while
|
117
|
+
while p.remainder != nil
|
68
118
|
p = p.remainder
|
69
119
|
end
|
70
120
|
p.first
|
71
121
|
end
|
72
122
|
|
123
|
+
def prepend(to_prepend)
|
124
|
+
pb = Hocon::Impl::PathBuilder.new
|
125
|
+
|
126
|
+
pb.append_path(to_prepend)
|
127
|
+
pb.append_path(self)
|
128
|
+
|
129
|
+
pb.result
|
130
|
+
end
|
131
|
+
|
73
132
|
def length
|
74
133
|
count = 1
|
75
134
|
p = remainder
|
76
|
-
while
|
135
|
+
while p != nil do
|
77
136
|
count += 1
|
78
137
|
p = p.remainder
|
79
138
|
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
#
|
84
|
-
# toString() is a debugging-oriented version while this is an
|
85
|
-
# error-message-oriented human-readable one.
|
86
|
-
#
|
87
|
-
def render
|
88
|
-
sb = StringIO.new
|
89
|
-
append_to_string_builder(sb)
|
90
|
-
sb.string
|
139
|
+
count
|
91
140
|
end
|
92
141
|
|
93
|
-
def
|
94
|
-
if
|
95
|
-
|
96
|
-
else
|
97
|
-
sb << @first
|
142
|
+
def sub_path(first_index, last_index)
|
143
|
+
if last_index < first_index
|
144
|
+
raise ConfigBugOrBrokenError.new("bad call to sub_path")
|
98
145
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
146
|
+
from = sub_path_to_end(first_index)
|
147
|
+
pb = Hocon::Impl::PathBuilder.new
|
148
|
+
count = last_index - first_index
|
149
|
+
while count > 0 do
|
150
|
+
count -= 1
|
151
|
+
pb.append_key(from.first)
|
152
|
+
from = from.remainder
|
153
|
+
if from.nil?
|
154
|
+
raise ConfigBugOrBrokenError.new("sub_path last_index out of range #{last_index}")
|
155
|
+
end
|
103
156
|
end
|
157
|
+
pb.result
|
104
158
|
end
|
105
159
|
|
160
|
+
# translated from `subPath(int removeFromFront)` upstream
|
106
161
|
def sub_path_to_end(remove_from_front)
|
107
162
|
count = remove_from_front
|
108
163
|
p = self
|
@@ -113,22 +168,88 @@ class Hocon::Impl::Path
|
|
113
168
|
p
|
114
169
|
end
|
115
170
|
|
116
|
-
def
|
117
|
-
|
118
|
-
|
171
|
+
def starts_with(other)
|
172
|
+
my_remainder = self
|
173
|
+
other_remainder = other
|
174
|
+
if other_remainder.length <= my_remainder.length
|
175
|
+
while ! other_remainder.nil?
|
176
|
+
if ! (other_remainder.first == my_remainder.first)
|
177
|
+
return false
|
178
|
+
end
|
179
|
+
my_remainder = my_remainder.remainder
|
180
|
+
other_remainder = other_remainder.remainder
|
181
|
+
end
|
182
|
+
return true
|
119
183
|
end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
184
|
+
false
|
185
|
+
end
|
186
|
+
|
187
|
+
def ==(other)
|
188
|
+
if other.is_a? Hocon::Impl::Path
|
189
|
+
that = other
|
190
|
+
first == that.first && ConfigImplUtil.equals_handling_nil?(remainder, that.remainder)
|
191
|
+
else
|
192
|
+
false
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def hash
|
197
|
+
remainder_hash = remainder.nil? ? 0 : remainder.hash
|
198
|
+
|
199
|
+
41 * (41 + first.hash) + remainder_hash
|
200
|
+
end
|
201
|
+
|
202
|
+
# this doesn't have a very precise meaning, just to reduce
|
203
|
+
# noise from quotes in the rendered path for average cases
|
204
|
+
def self.has_funky_chars?(s)
|
205
|
+
length = s.length
|
206
|
+
if length == 0
|
207
|
+
return false
|
208
|
+
end
|
209
|
+
|
210
|
+
s.chars.each do |c|
|
211
|
+
unless (c =~ /[[:alnum:]]/) || (c == '-') || (c == '_')
|
212
|
+
return true
|
129
213
|
end
|
130
214
|
end
|
131
|
-
|
215
|
+
|
216
|
+
false
|
217
|
+
end
|
218
|
+
|
219
|
+
def append_to_string_builder(sb)
|
220
|
+
if self.class.has_funky_chars?(@first) || @first.empty?
|
221
|
+
sb << ConfigImplUtil.render_json_string(@first)
|
222
|
+
else
|
223
|
+
sb << @first
|
224
|
+
end
|
225
|
+
|
226
|
+
unless @remainder.nil?
|
227
|
+
sb << "."
|
228
|
+
@remainder.append_to_string_builder(sb)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def to_s
|
233
|
+
sb = StringIO.new
|
234
|
+
sb << "Path("
|
235
|
+
append_to_string_builder(sb)
|
236
|
+
sb << ")"
|
237
|
+
|
238
|
+
sb.string
|
239
|
+
end
|
240
|
+
|
241
|
+
def inspect
|
242
|
+
to_s
|
243
|
+
end
|
244
|
+
|
245
|
+
#
|
246
|
+
# toString() is a debugging-oriented version while this is an
|
247
|
+
# error-message-oriented human-readable one.
|
248
|
+
#
|
249
|
+
def render
|
250
|
+
sb = StringIO.new
|
251
|
+
append_to_string_builder(sb)
|
252
|
+
sb.string
|
132
253
|
end
|
133
254
|
|
134
255
|
def self.new_key(key)
|
@@ -136,6 +257,7 @@ class Hocon::Impl::Path
|
|
136
257
|
end
|
137
258
|
|
138
259
|
def self.new_path(path)
|
139
|
-
Hocon::Impl::
|
260
|
+
Hocon::Impl::PathParser.parse_path(path)
|
140
261
|
end
|
141
|
-
|
262
|
+
|
263
|
+
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
2
4
|
require 'hocon/impl/path'
|
5
|
+
require 'hocon/config_error'
|
3
6
|
|
4
7
|
class Hocon::Impl::PathBuilder
|
5
8
|
|
@@ -10,7 +13,7 @@ class Hocon::Impl::PathBuilder
|
|
10
13
|
|
11
14
|
def check_can_append
|
12
15
|
if @result
|
13
|
-
raise
|
16
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "Adding to PathBuilder after getting result"
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
@@ -19,8 +22,26 @@ class Hocon::Impl::PathBuilder
|
|
19
22
|
@keys.push(key)
|
20
23
|
end
|
21
24
|
|
25
|
+
def append_path(path)
|
26
|
+
check_can_append
|
27
|
+
|
28
|
+
first = path.first
|
29
|
+
remainder = path.remainder
|
30
|
+
|
31
|
+
loop do
|
32
|
+
@keys.push(first)
|
33
|
+
|
34
|
+
if !remainder.nil?
|
35
|
+
first = remainder.first
|
36
|
+
remainder = remainder.remainder
|
37
|
+
else
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
22
43
|
def result
|
23
|
-
# note: if keys is empty, we want to return
|
44
|
+
# note: if keys is empty, we want to return nil, which is a valid
|
24
45
|
# empty path
|
25
46
|
if @result.nil?
|
26
47
|
remainder = nil
|
@@ -32,4 +53,4 @@ class Hocon::Impl::PathBuilder
|
|
32
53
|
end
|
33
54
|
@result
|
34
55
|
end
|
35
|
-
end
|
56
|
+
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
require 'hocon/impl'
|
5
|
+
require 'hocon/config_syntax'
|
6
|
+
require 'hocon/impl/tokenizer'
|
7
|
+
require 'hocon/impl/config_node_path'
|
8
|
+
require 'hocon/impl/tokens'
|
9
|
+
require 'hocon/config_value_type'
|
10
|
+
require 'hocon/config_error'
|
11
|
+
|
12
|
+
class Hocon::Impl::PathParser
|
13
|
+
ConfigSyntax = Hocon::ConfigSyntax
|
14
|
+
SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
|
15
|
+
Tokenizer = Hocon::Impl::Tokenizer
|
16
|
+
Tokens = Hocon::Impl::Tokens
|
17
|
+
ConfigNodePath = Hocon::Impl::ConfigNodePath
|
18
|
+
ConfigValueType = Hocon::ConfigValueType
|
19
|
+
ConfigBadPathError = Hocon::ConfigError::ConfigBadPathError
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
class Element
|
24
|
+
def initialize(initial, can_be_empty)
|
25
|
+
@can_be_empty = can_be_empty
|
26
|
+
@sb = StringIO.new(initial)
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :can_be_empty, :sb
|
30
|
+
|
31
|
+
def to_string
|
32
|
+
"Element(#{@sb.string},#{@can_be_empty})"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.api_origin
|
37
|
+
SimpleConfigOrigin.new_simple("path parameter")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.parse_path_node(path, flavor = ConfigSyntax::CONF)
|
41
|
+
reader = StringIO.new(path)
|
42
|
+
|
43
|
+
begin
|
44
|
+
tokens = Tokenizer.tokenize(api_origin, reader,
|
45
|
+
flavor)
|
46
|
+
tokens.next # drop START
|
47
|
+
parse_path_node_expression(tokens, api_origin, path, flavor)
|
48
|
+
ensure
|
49
|
+
reader.close
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.parse_path(path)
|
54
|
+
speculated = speculative_fast_parse_path(path)
|
55
|
+
if not speculated.nil?
|
56
|
+
return speculated
|
57
|
+
end
|
58
|
+
|
59
|
+
reader = StringIO.new(path)
|
60
|
+
|
61
|
+
begin
|
62
|
+
tokens = Tokenizer.tokenize(api_origin, reader, ConfigSyntax::CONF)
|
63
|
+
tokens.next # drop START
|
64
|
+
return parse_path_expression(tokens, api_origin, path)
|
65
|
+
ensure
|
66
|
+
reader.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.parse_path_node_expression(expression, origin, original_text = nil,
|
71
|
+
flavor = ConfigSyntax::CONF)
|
72
|
+
path_tokens = []
|
73
|
+
path = parse_path_expression(expression, origin, original_text, path_tokens, flavor)
|
74
|
+
ConfigNodePath.new(path, path_tokens);
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.parse_path_expression(expression, origin, original_text = nil,
|
78
|
+
path_tokens = nil, flavor = ConfigSyntax::CONF)
|
79
|
+
# each builder in "buf" is an element in the path
|
80
|
+
buf = []
|
81
|
+
buf.push(Element.new("", false))
|
82
|
+
|
83
|
+
if !expression.has_next?
|
84
|
+
raise ConfigBadPathError.new(
|
85
|
+
origin,
|
86
|
+
original_text,
|
87
|
+
"Expecting a field name or path here, but got nothing")
|
88
|
+
end
|
89
|
+
|
90
|
+
while expression.has_next?
|
91
|
+
t = expression.next
|
92
|
+
|
93
|
+
if ! path_tokens.nil?
|
94
|
+
path_tokens << t
|
95
|
+
end
|
96
|
+
|
97
|
+
# Ignore all IgnoredWhitespace tokens
|
98
|
+
next if Tokens.ignored_whitespace?(t)
|
99
|
+
|
100
|
+
if Tokens.value_with_type?(t, ConfigValueType::STRING)
|
101
|
+
v = Tokens.value(t)
|
102
|
+
# this is a quoted string; so any periods
|
103
|
+
# in here don't count as path separators
|
104
|
+
s = v.transform_to_string
|
105
|
+
add_path_text(buf, true, s)
|
106
|
+
elsif t == Tokens::EOF
|
107
|
+
# ignore this; when parsing a file, it should not happen
|
108
|
+
# since we're parsing a token list rather than the main
|
109
|
+
# token iterator, and when parsing a path expression from the
|
110
|
+
# API, it's expected to have an EOF.
|
111
|
+
else
|
112
|
+
# any periods outside of a quoted string count as
|
113
|
+
# separators
|
114
|
+
text = nil
|
115
|
+
if Tokens.value?(t)
|
116
|
+
# appending a number here may add
|
117
|
+
# a period, but we _do_ count those as path
|
118
|
+
# separators, because we basically want
|
119
|
+
# "foo 3.0bar" to parse as a string even
|
120
|
+
# though there's a number in it. The fact that
|
121
|
+
# we tokenize non-string values is largely an
|
122
|
+
# implementation detail.
|
123
|
+
v = Tokens.value(t)
|
124
|
+
|
125
|
+
# We need to split the tokens on a . so that we can get sub-paths but still preserve
|
126
|
+
# the original path text when doing an insertion
|
127
|
+
if ! path_tokens.nil?
|
128
|
+
path_tokens.delete_at(path_tokens.size - 1)
|
129
|
+
path_tokens.concat(split_token_on_period(t, flavor))
|
130
|
+
end
|
131
|
+
text = v.transform_to_string
|
132
|
+
elsif Tokens.unquoted_text?(t)
|
133
|
+
# We need to split the tokens on a . so that we can get sub-paths but still preserve
|
134
|
+
# the original path text when doing an insertion on ConfigNodeObjects
|
135
|
+
if ! path_tokens.nil?
|
136
|
+
path_tokens.delete_at(path_tokens.size - 1)
|
137
|
+
path_tokens.concat(split_token_on_period(t, flavor))
|
138
|
+
end
|
139
|
+
text = Tokens.unquoted_text(t)
|
140
|
+
else
|
141
|
+
raise ConfigBadPathError.new(
|
142
|
+
origin,
|
143
|
+
original_text,
|
144
|
+
"Token not allowed in path expression: #{t}" +
|
145
|
+
" (you can double-quote this token if you really want it here)")
|
146
|
+
end
|
147
|
+
|
148
|
+
add_path_text(buf, false, text)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
pb = Hocon::Impl::PathBuilder.new
|
153
|
+
buf.each do |e|
|
154
|
+
if (e.sb.length == 0) && !e.can_be_empty
|
155
|
+
raise Hocon::ConfigError::ConfigBadPathError.new(
|
156
|
+
origin,
|
157
|
+
original_text,
|
158
|
+
"path has a leading, trailing, or two adjacent period '.' (use quoted \"\" empty string if you want an empty element)")
|
159
|
+
else
|
160
|
+
pb.append_key(e.sb.string)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
pb.result
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.split_token_on_period(t, flavor)
|
168
|
+
token_text = t.token_text
|
169
|
+
if token_text == "."
|
170
|
+
return [t]
|
171
|
+
end
|
172
|
+
split_token = token_text.split('.')
|
173
|
+
split_tokens = []
|
174
|
+
split_token.each do |s|
|
175
|
+
if flavor == ConfigSyntax::CONF
|
176
|
+
split_tokens << Tokens.new_unquoted_text(t.origin, s)
|
177
|
+
else
|
178
|
+
split_tokens << Tokens.new_string(t.origin, s, "\"#{s}\"")
|
179
|
+
end
|
180
|
+
split_tokens << Tokens.new_unquoted_text(t.origin, ".")
|
181
|
+
end
|
182
|
+
if token_text[-1] != "."
|
183
|
+
split_tokens.delete_at(split_tokens.size - 1)
|
184
|
+
end
|
185
|
+
split_tokens
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.add_path_text(buf, was_quoted, new_text)
|
189
|
+
i = if was_quoted
|
190
|
+
-1
|
191
|
+
else
|
192
|
+
new_text.index('.') || -1
|
193
|
+
end
|
194
|
+
current = buf.last
|
195
|
+
if i < 0
|
196
|
+
# add to current path element
|
197
|
+
current.sb << new_text
|
198
|
+
# any empty quoted string means this element can
|
199
|
+
# now be empty.
|
200
|
+
if was_quoted && (current.sb.length == 0)
|
201
|
+
current.can_be_empty = true
|
202
|
+
end
|
203
|
+
else
|
204
|
+
# "buf" plus up to the period is an element
|
205
|
+
current.sb << new_text[0, i]
|
206
|
+
# then start a new element
|
207
|
+
buf.push(Element.new("", false))
|
208
|
+
# recurse to consume remainder of new_text
|
209
|
+
add_path_text(buf, false, new_text[i + 1, new_text.length - 1])
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# the idea is to see if the string has any chars or features
|
214
|
+
# that might require the full parser to deal with.
|
215
|
+
def self.looks_unsafe_for_fast_parser(s)
|
216
|
+
last_was_dot = true # // start of path is also a "dot"
|
217
|
+
len = s.length
|
218
|
+
if s.empty?
|
219
|
+
return true
|
220
|
+
end
|
221
|
+
if s[0] == "."
|
222
|
+
return true
|
223
|
+
end
|
224
|
+
if s[len - 1] == "."
|
225
|
+
return true
|
226
|
+
end
|
227
|
+
|
228
|
+
(0..len).each do |i|
|
229
|
+
c = s[i]
|
230
|
+
if c =~ /^\w$/
|
231
|
+
last_was_dot = false
|
232
|
+
next
|
233
|
+
elsif c == '.'
|
234
|
+
if last_was_dot
|
235
|
+
return true # ".." means we need to throw an error
|
236
|
+
end
|
237
|
+
last_was_dot = true
|
238
|
+
elsif c == '-'
|
239
|
+
if last_was_dot
|
240
|
+
return true
|
241
|
+
end
|
242
|
+
next
|
243
|
+
else
|
244
|
+
return true
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
if last_was_dot
|
249
|
+
return true
|
250
|
+
end
|
251
|
+
|
252
|
+
false
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.fast_path_build(tail, s, path_end)
|
256
|
+
# rindex takes last index it should look at, end - 1 not end
|
257
|
+
split_at = s.rindex(".", path_end - 1)
|
258
|
+
tokens = []
|
259
|
+
tokens << Tokens.new_unquoted_text(nil, s)
|
260
|
+
# this works even if split_at is -1; then we start the substring at 0
|
261
|
+
with_one_more_element = Path.new(s[split_at + 1..path_end], tail)
|
262
|
+
if split_at < 0
|
263
|
+
with_one_more_element
|
264
|
+
else
|
265
|
+
fast_path_build(with_one_more_element, s, split_at)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# do something much faster than the full parser if
|
270
|
+
# we just have something like "foo" or "foo.bar"
|
271
|
+
def self.speculative_fast_parse_path(path)
|
272
|
+
s = Hocon::Impl::ConfigImplUtil.unicode_trim(path)
|
273
|
+
if looks_unsafe_for_fast_parser(s)
|
274
|
+
return nil
|
275
|
+
end
|
276
|
+
|
277
|
+
fast_path_build(nil, s, s.length)
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|