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
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
|