hocon 0.0.1
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.
- data/LICENSE +202 -0
- data/README.md +19 -0
- data/lib/hocon.rb +2 -0
- data/lib/hocon/config_error.rb +12 -0
- data/lib/hocon/config_factory.rb +9 -0
- data/lib/hocon/config_object.rb +4 -0
- data/lib/hocon/config_parse_options.rb +53 -0
- data/lib/hocon/config_render_options.rb +46 -0
- data/lib/hocon/config_syntax.rb +7 -0
- data/lib/hocon/config_value_type.rb +26 -0
- data/lib/hocon/impl.rb +5 -0
- data/lib/hocon/impl/abstract_config_object.rb +64 -0
- data/lib/hocon/impl/abstract_config_value.rb +130 -0
- data/lib/hocon/impl/config_concatenation.rb +136 -0
- data/lib/hocon/impl/config_float.rb +9 -0
- data/lib/hocon/impl/config_impl.rb +10 -0
- data/lib/hocon/impl/config_impl_util.rb +78 -0
- data/lib/hocon/impl/config_int.rb +31 -0
- data/lib/hocon/impl/config_number.rb +27 -0
- data/lib/hocon/impl/config_string.rb +37 -0
- data/lib/hocon/impl/full_includer.rb +4 -0
- data/lib/hocon/impl/origin_type.rb +9 -0
- data/lib/hocon/impl/parseable.rb +151 -0
- data/lib/hocon/impl/parser.rb +882 -0
- data/lib/hocon/impl/path.rb +59 -0
- data/lib/hocon/impl/path_builder.rb +36 -0
- data/lib/hocon/impl/resolve_status.rb +18 -0
- data/lib/hocon/impl/simple_config.rb +11 -0
- data/lib/hocon/impl/simple_config_list.rb +70 -0
- data/lib/hocon/impl/simple_config_object.rb +178 -0
- data/lib/hocon/impl/simple_config_origin.rb +174 -0
- data/lib/hocon/impl/simple_include_context.rb +7 -0
- data/lib/hocon/impl/simple_includer.rb +19 -0
- data/lib/hocon/impl/token.rb +32 -0
- data/lib/hocon/impl/token_type.rb +42 -0
- data/lib/hocon/impl/tokenizer.rb +370 -0
- data/lib/hocon/impl/tokens.rb +157 -0
- data/lib/hocon/impl/unmergeable.rb +4 -0
- metadata +84 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'hocon/impl'
|
2
|
+
require 'stringio'
|
3
|
+
require 'hocon/config_render_options'
|
4
|
+
require 'hocon/config_object'
|
5
|
+
require 'hocon/impl/resolve_status'
|
6
|
+
require 'hocon/impl/unmergeable'
|
7
|
+
require 'hocon/impl/abstract_config_object'
|
8
|
+
require 'hocon/impl/config_impl_util'
|
9
|
+
|
10
|
+
##
|
11
|
+
## Trying very hard to avoid a parent reference in config values; when you have
|
12
|
+
## a tree like this, the availability of parent() tends to result in a lot of
|
13
|
+
## improperly-factored and non-modular code. Please don't add parent().
|
14
|
+
##
|
15
|
+
class Hocon::Impl::AbstractConfigValue
|
16
|
+
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
|
17
|
+
|
18
|
+
def initialize(origin)
|
19
|
+
@origin = origin
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :origin
|
23
|
+
|
24
|
+
def resolve_status
|
25
|
+
Hocon::Impl::ResolveStatus::RESOLVED
|
26
|
+
end
|
27
|
+
|
28
|
+
# this is virtualized rather than a field because only some subclasses
|
29
|
+
# really need to store the boolean, and they may be able to pack it
|
30
|
+
# with another boolean to save space.
|
31
|
+
def ignores_fallbacks?
|
32
|
+
# if we are not resolved, then somewhere in this value there's
|
33
|
+
# a substitution that may need to look at the fallbacks.
|
34
|
+
resolve_status == Hocon::Impl::ResolveStatus::RESOLVED
|
35
|
+
end
|
36
|
+
|
37
|
+
# the withFallback() implementation is supposed to avoid calling
|
38
|
+
# mergedWith* if we're ignoring fallbacks.
|
39
|
+
def require_not_ignoring_fallbacks
|
40
|
+
if ignores_fallbacks?
|
41
|
+
raise ConfigBugError, "method should not have been called with ignoresFallbacks=true #{self.class.name}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def with_origin(origin)
|
46
|
+
if @origin == origin
|
47
|
+
self
|
48
|
+
else
|
49
|
+
new_copy(origin)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_fallback(mergeable)
|
54
|
+
if ignores_fallbacks?
|
55
|
+
self
|
56
|
+
else
|
57
|
+
other = mergeable.to_fallback_value
|
58
|
+
if other.is_a?(Hocon::Impl::Unmergeable)
|
59
|
+
merged_with_the_unmergeable(other)
|
60
|
+
elsif other.is_a?(Hocon::Impl::AbstractConfigObject)
|
61
|
+
merged_with_object(other)
|
62
|
+
else
|
63
|
+
merged_with_non_object(other)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
sb = StringIO.new
|
70
|
+
render_to_sb(sb, 0, true, nil, Hocon::ConfigRenderOptions.concise)
|
71
|
+
"#{self.class.name}(#{sb.string})"
|
72
|
+
end
|
73
|
+
|
74
|
+
def indent(sb, indent_size, options)
|
75
|
+
if options.formatted?
|
76
|
+
remaining = indent_size
|
77
|
+
while remaining > 0
|
78
|
+
sb << " "
|
79
|
+
remaining -= 1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def render_to_sb(sb, indent, at_root, at_key, options)
|
85
|
+
if !at_key.nil?
|
86
|
+
rendered_key =
|
87
|
+
if options.json?
|
88
|
+
ConfigImplUtil.render_json_string(at_key)
|
89
|
+
else
|
90
|
+
ConfigImplUtil.render_string_unquoted_if_possible(at_key)
|
91
|
+
end
|
92
|
+
|
93
|
+
sb << rendered_key
|
94
|
+
|
95
|
+
if options.json?
|
96
|
+
if options.formatted?
|
97
|
+
sb << " : "
|
98
|
+
else
|
99
|
+
sb << ":"
|
100
|
+
end
|
101
|
+
else
|
102
|
+
# in non-JSON we can omit the colon or equals before an object
|
103
|
+
if self.is_a?(Hocon::ConfigObject)
|
104
|
+
if options.formatted?
|
105
|
+
sb << ' '
|
106
|
+
end
|
107
|
+
else
|
108
|
+
sb << "="
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
render_value_to_sb(sb, indent, at_root, options)
|
113
|
+
end
|
114
|
+
|
115
|
+
# to be overridden by subclasses
|
116
|
+
def render_value_to_sb(sb, indent, at_root, options)
|
117
|
+
u = unwrapped
|
118
|
+
sb << u.to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
def render(options = Hocon::ConfigRenderOptions.defaults)
|
122
|
+
sb = StringIO.new
|
123
|
+
render_to_sb(sb, 0, true, nil, options)
|
124
|
+
# We take a substring that ends at sb.pos, because we've been decrementing
|
125
|
+
# sb.pos at various points in the code as a means to remove characters from
|
126
|
+
# the end of the StringIO
|
127
|
+
sb.string[0, sb.pos]
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'hocon/impl'
|
2
|
+
require 'hocon/impl/abstract_config_value'
|
3
|
+
require 'hocon/impl/abstract_config_object'
|
4
|
+
require 'hocon/impl/simple_config_list'
|
5
|
+
require 'hocon/config_object'
|
6
|
+
require 'hocon/impl/unmergeable'
|
7
|
+
require 'hocon/impl/simple_config_origin'
|
8
|
+
require 'hocon/impl/config_string'
|
9
|
+
|
10
|
+
class Hocon::Impl::ConfigConcatenation < Hocon::Impl::AbstractConfigValue
|
11
|
+
include Hocon::Impl::Unmergeable
|
12
|
+
|
13
|
+
SimpleConfigList = Hocon::Impl::SimpleConfigList
|
14
|
+
ConfigObject = Hocon::ConfigObject
|
15
|
+
Unmergeable = Hocon::Impl::Unmergeable
|
16
|
+
SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
|
17
|
+
|
18
|
+
#
|
19
|
+
# Add left and right, or their merger, to builder
|
20
|
+
#
|
21
|
+
def self.join(builder, orig_right)
|
22
|
+
left = builder[builder.size - 1]
|
23
|
+
right = orig_right
|
24
|
+
|
25
|
+
# check for an object which can be converted to a list
|
26
|
+
# (this will be an object with numeric keys, like foo.0, foo.1)
|
27
|
+
if (left.is_a?(ConfigObject)) && (right.is_a?(SimpleConfigList))
|
28
|
+
left = DefaultTransformer.transform(left, ConfigValueType::LIST)
|
29
|
+
elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(ConfigObject))
|
30
|
+
right = DefaultTransformer.transform(right, ConfigValueType::LIST)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Since this depends on the type of two instances, I couldn't think
|
34
|
+
# of much alternative to an instanceof chain. Visitors are sometimes
|
35
|
+
# used for multiple dispatch but seems like overkill.
|
36
|
+
joined = nil
|
37
|
+
if (left.is_a?(ConfigObject)) && (right.is_a?(ConfigObject))
|
38
|
+
joined = right.with_fallback(left)
|
39
|
+
elsif (left.is_a?(SimpleConfigList)) && (right.is_a?(SimpleConfigList))
|
40
|
+
joined = left.concatenate(right)
|
41
|
+
elsif (left.is_a?(Hocon::Impl::ConfigConcatenation)) ||
|
42
|
+
(right.is_a?(Hocon::Impl::ConfigConcatenation))
|
43
|
+
raise ConfigBugError, "unflattened ConfigConcatenation"
|
44
|
+
elsif (left.is_a?(Unmergeable)) || (right.is_a?(Unmergeable))
|
45
|
+
# leave joined=null, cannot join
|
46
|
+
else
|
47
|
+
# handle primitive type or primitive type mixed with object or list
|
48
|
+
s1 = left.transform_to_string
|
49
|
+
s2 = right.transform_to_string
|
50
|
+
if s1.nil? || s2.nil?
|
51
|
+
raise ConfigWrongTypeError.new(left.origin,
|
52
|
+
"Cannot concatenate object or list with a non-object-or-list, #{left} " +
|
53
|
+
"and #{right} are not compatible")
|
54
|
+
else
|
55
|
+
joined_origin = SimpleConfigOrigin.merge_origins([left.origin, right.origin])
|
56
|
+
joined = Hocon::Impl::ConfigString.new(joined_origin, s1 + s2)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if joined.nil?
|
61
|
+
builder.push(right)
|
62
|
+
else
|
63
|
+
builder.pop
|
64
|
+
builder.push(joined)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.consolidate(pieces)
|
69
|
+
if pieces.length < 2
|
70
|
+
pieces
|
71
|
+
else
|
72
|
+
flattened = []
|
73
|
+
pieces.each do |v|
|
74
|
+
if v.is_a?(Hocon::Impl::ConfigConcatenation)
|
75
|
+
flattened.concat(v.pieces)
|
76
|
+
else
|
77
|
+
flattened.push(v)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
consolidated = []
|
82
|
+
flattened.each do |v|
|
83
|
+
if consolidated.empty?
|
84
|
+
consolidated.push(v)
|
85
|
+
else
|
86
|
+
join(consolidated, v)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
consolidated
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.concatenate(pieces)
|
95
|
+
consolidated = consolidate(pieces)
|
96
|
+
if consolidated.empty?
|
97
|
+
nil
|
98
|
+
elsif consolidated.length == 1
|
99
|
+
consolidated[0]
|
100
|
+
else
|
101
|
+
merged_origin = SimpleConfigOrigin.merge_origins(consolidated)
|
102
|
+
Hocon::Impl::ConfigConcatenation.new(merged_origin, consolidated)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def initialize(origin, pieces)
|
108
|
+
super(origin)
|
109
|
+
@pieces = pieces
|
110
|
+
|
111
|
+
if pieces.size < 2
|
112
|
+
raise ConfigBugError, "Created concatenation with less than 2 items: #{self}"
|
113
|
+
end
|
114
|
+
|
115
|
+
had_unmergeable = false
|
116
|
+
pieces.each do |p|
|
117
|
+
if p.is_a?(Hocon::Impl::ConfigConcatenation)
|
118
|
+
raise ConfigBugError, "ConfigConcatenation should never be nested: #{self}"
|
119
|
+
end
|
120
|
+
if p.is_a?(Unmergeable)
|
121
|
+
had_unmergeable = true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
unless had_unmergeable
|
126
|
+
raise ConfigBugError, "Created concatenation without an unmergeable in it: #{self}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def ignores_fallbacks?
|
131
|
+
# we can never ignore fallbacks because if a child ConfigReference
|
132
|
+
# is self-referential we have to look lower in the merge stack
|
133
|
+
# for its value.
|
134
|
+
false
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'hocon/impl'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class Hocon::Impl::ConfigImplUtil
|
5
|
+
def self.equals_handling_nil?(a, b)
|
6
|
+
# This method probably doesn't make any sense in ruby... not sure
|
7
|
+
if a.nil? && !b.nil?
|
8
|
+
false
|
9
|
+
elsif !a.nil? && b.nil?
|
10
|
+
false
|
11
|
+
# in ruby, the == and .equal? are the opposite of what they are in Java
|
12
|
+
elsif a.equal?(b)
|
13
|
+
true
|
14
|
+
else
|
15
|
+
a == b
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# This is public ONLY for use by the "config" package, DO NOT USE this ABI
|
21
|
+
# may change.
|
22
|
+
#
|
23
|
+
def self.render_json_string(s)
|
24
|
+
sb = StringIO.new
|
25
|
+
sb << '"'
|
26
|
+
s.chars.each do |c|
|
27
|
+
case c
|
28
|
+
when '"' then sb << "\\\""
|
29
|
+
when "\\" then sb << "\\\\"
|
30
|
+
when "\n" then sb << "\\n"
|
31
|
+
when "\b" then sb << "\\b"
|
32
|
+
when "\f" then sb << "\\f"
|
33
|
+
when "\r" then sb << "\\r"
|
34
|
+
when "\t" then sb << "\\t"
|
35
|
+
else
|
36
|
+
if c =~ /[[:cntrl:]]/
|
37
|
+
sb << ("\\u%04x" % c)
|
38
|
+
else
|
39
|
+
sb << c
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
sb << '"'
|
44
|
+
sb.string
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.render_string_unquoted_if_possible(s)
|
48
|
+
# this can quote unnecessarily as long as it never fails to quote when
|
49
|
+
# necessary
|
50
|
+
if s.length == 0
|
51
|
+
return render_json_string(s)
|
52
|
+
end
|
53
|
+
|
54
|
+
# if it starts with a hyphen or number, we have to quote
|
55
|
+
# to ensure we end up with a string and not a number
|
56
|
+
first = s.chars.first
|
57
|
+
if (first =~ /[[:digit:]]/) || (first == '-')
|
58
|
+
return render_json_string(s)
|
59
|
+
end
|
60
|
+
|
61
|
+
# only unquote if it's pure alphanumeric
|
62
|
+
s.chars.each do |c|
|
63
|
+
unless (c =~ /[[:alnum:]]/) || (c == '-')
|
64
|
+
return render_json_string(s)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
s
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.whitespace?(c)
|
72
|
+
# this implementation is *not* a port of the java code, because it relied on
|
73
|
+
# the method java.lang.Character#isWhitespace. This is probably
|
74
|
+
# insanely slow (running a regex against every single character in the
|
75
|
+
# file).
|
76
|
+
c =~ /[[:space:]]/
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'hocon/impl'
|
2
|
+
require 'hocon/impl/config_number'
|
3
|
+
require 'hocon/config_value_type'
|
4
|
+
|
5
|
+
class Hocon::Impl::ConfigInt < Hocon::Impl::ConfigNumber
|
6
|
+
def initialize(origin, value, original_text)
|
7
|
+
super(origin, original_text)
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def value_type
|
12
|
+
Hocon::ConfigValueType::NUMBER
|
13
|
+
end
|
14
|
+
|
15
|
+
def unwrapped
|
16
|
+
@value
|
17
|
+
end
|
18
|
+
|
19
|
+
def transform_to_string
|
20
|
+
s = super
|
21
|
+
if s.nil?
|
22
|
+
self.to_s
|
23
|
+
else
|
24
|
+
s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def new_copy(origin)
|
29
|
+
Hocon::Impl::ConfigInt.new(origin, @value, @original_text)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'hocon/impl'
|
2
|
+
require 'hocon/impl/abstract_config_value'
|
3
|
+
|
4
|
+
class Hocon::Impl::ConfigNumber < Hocon::Impl::AbstractConfigValue
|
5
|
+
## sigh... requiring these subclasses before this class
|
6
|
+
## is declared would cause an error. Thanks, ruby.
|
7
|
+
require 'hocon/impl/config_int'
|
8
|
+
require 'hocon/impl/config_float'
|
9
|
+
|
10
|
+
def self.new_number(origin, number, original_text)
|
11
|
+
as_int = number.to_i
|
12
|
+
if as_int == number
|
13
|
+
Hocon::Impl::ConfigInt.new(origin, as_int, original_text)
|
14
|
+
else
|
15
|
+
Hocon::Impl::ConfigFloat.new(origin, number, original_text)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(origin, original_text)
|
20
|
+
super(origin)
|
21
|
+
@original_text = original_text
|
22
|
+
end
|
23
|
+
|
24
|
+
def transform_to_string
|
25
|
+
@original_text
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'hocon/impl'
|
2
|
+
require 'hocon/impl/abstract_config_value'
|
3
|
+
require 'hocon/config_value_type'
|
4
|
+
require 'hocon/impl/config_impl_util'
|
5
|
+
|
6
|
+
class Hocon::Impl::ConfigString < Hocon::Impl::AbstractConfigValue
|
7
|
+
ConfigImplUtil = Hocon::Impl::ConfigImplUtil
|
8
|
+
|
9
|
+
def initialize(origin, value)
|
10
|
+
super(origin)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def value_type
|
15
|
+
Hocon::ConfigValueType::STRING
|
16
|
+
end
|
17
|
+
|
18
|
+
def unwrapped
|
19
|
+
@value
|
20
|
+
end
|
21
|
+
|
22
|
+
def transform_to_string
|
23
|
+
@value
|
24
|
+
end
|
25
|
+
|
26
|
+
def render_value_to_sb(sb, indent_size, at_root, options)
|
27
|
+
if options.json?
|
28
|
+
sb << ConfigImplUtil.render_json_string(@value)
|
29
|
+
else
|
30
|
+
sb << ConfigImplUtil.render_string_unquoted_if_possible(@value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def new_copy(origin)
|
35
|
+
Hocon::Impl::ConfigString.new(origin, @value)
|
36
|
+
end
|
37
|
+
end
|