1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Handle ini config files.
18 # A configuration tree that can read and store data in ini format
22 # var config = new ConfigTree("config.ini")
23 # config["goo"] = "goo"
24 # config["foo.bar"] = "foobar"
25 # config["foo.baz"] = "foobaz"
27 # assert config.to_map.length == 3
31 # config = new ConfigTree("config.ini")
32 # assert config.has_key("foo.bar")
33 # assert config["foo.bar"] == "foobar"
37 # The ini file used to read/store data
40 init do if ini_file
.file_exists
then load
42 # Get the config value for `key`
44 # REQUIRE: `has_key(key)`
46 # var config = new ConfigTree("config.ini")
47 # assert config["goo"] == "goo"
48 # assert config["foo.bar"] == "foobar"
49 # assert config["foo.baz"] == "foobaz"
50 fun [](key
: String): String do
51 if not has_key
(key
) then
52 print
"error: config key `{key}` not found"
55 var node
= get_node
(key
).as(not null)
56 if node
.value
== null then
57 print
"error: config key `{key}` has no value"
60 return node
.value
.as(not null)
63 # Get the config values under `key`
65 # REQUIRE: `has_key(key)`
67 # var config = new ConfigTree("config.ini")
68 # var values = config.at("foo")
69 # assert values.has_key("bar")
70 # assert values.has_key("baz")
71 # assert not values.has_key("goo")
72 fun at
(key
: String): Map[String, String] do
73 if not has_key
(key
) then
74 print
"error: config key `{key}` not found"
77 var map
= new HashMap[String, String]
78 var node
= get_node
(key
).as(not null)
79 for k
, child
in node
.children
do
80 if child
.value
== null then continue
81 map
[k
] = child
.value
.to_s
86 # Set `value` at `key`
88 # var config = new ConfigTree("config.ini")
89 # assert config["foo.bar"] == "foobar"
90 # config["foo.bar"] = "baz"
91 # assert config["foo.bar"] == "baz"
92 fun []=(key
: String, value
: nullable String) do
96 # Is `key` in the config?
98 # var config = new ConfigTree("config.ini")
99 # assert config.has_key("goo")
100 # assert config.has_key("foo.bar")
101 # assert not config.has_key("zoo")
102 fun has_key
(key
: String): Bool do
103 var parts
= key
.split
(".").reversed
104 var node
= get_root
(parts
.pop
)
105 if node
== null then return false
106 while not parts
.is_empty
do
107 node
= node
.get_child
(parts
.pop
)
108 if node
== null then return false
113 # Get `self` as a Map of `key`, `value`
115 # var config = new ConfigTree("config.ini")
116 # var map = config.to_map
117 # assert map.has_key("goo")
118 # assert map.has_key("foo.bar")
119 # assert map.has_key("foo.baz")
120 # assert map.length == 3
121 fun to_map
: Map[String, String] do
122 var map
= new HashMap[String, String]
123 for node
in leaves
do
124 if node
.value
== null then continue
125 map
[node
.key
] = node
.value
.to_s
130 redef fun to_s
do return to_map
.join
(", ", ":")
132 redef fun write_to
(stream
) do
133 for node
in leaves
do
134 if node
.value
== null then continue
135 stream
.write
("{node.key}={node.value.to_s}\n")
139 # Reload config from file
140 # Done automatically at init
142 # Example with hierarchical ini file:
149 # str.write_to_file("config1.ini")
151 # var config = new ConfigTree("config1.ini")
152 # assert config["foo.bar"] == "foobar"
154 # Example with sections:
164 # str.write_to_file("config2.ini")
166 # config = new ConfigTree("config2.ini")
167 # assert config["foo.bar"] == "foobar"
168 # assert config["boo.bar"] == "boobar"
170 # Example with both hierarchy and section:
179 # baz.bar=gooboobazbar"""
180 # str.write_to_file("config3.ini")
182 # config = new ConfigTree("config3.ini")
183 # assert config["goo"] == "goo"
184 # assert config["foo.bar.baz"] == "foobarbaz"
185 # assert config["goo.boo.bar"] == "gooboobar"
186 # assert config["goo.boo.baz.bar"] == "gooboobazbar"
189 var stream
= new FileReader.open
(ini_file
)
190 var path
: nullable String = null
191 while not stream
.eof
do
192 var line
= stream
.read_line
193 if line
.is_empty
then
195 else if line
.has_prefix
(";") then
197 else if line
.has_prefix
("[") then
199 var key
= line
.substring
(1, line
.length
- 2)
203 var parts
= line
.split
("=")
204 var key
= parts
[0].trim
205 var val
= parts
[1].trim
209 set_node
("{path}.{key}", val
)
216 # Save config to file
217 fun save
do write_to_file
(ini_file
)
219 private var roots
= new Array[ConfigNode]
221 private fun set_node
(key
: String, value
: nullable String) do
222 var parts
= key
.split
(".").reversed
224 var root
= get_root
(k
)
226 root
= new ConfigNode(k
)
227 if parts
.is_empty
then
232 while not parts
.is_empty
do
234 var node
= root
.get_child
(k
)
236 node
= new ConfigNode(k
)
238 root
.children
[node
.name
] = node
240 if parts
.is_empty
then
247 private fun get_node
(key
: String): nullable ConfigNode do
248 var parts
= key
.split
(".").reversed
249 var node
= get_root
(parts
.pop
)
250 while not parts
.is_empty
do
251 node
= node
.get_child
(parts
.pop
)
256 private fun get_root
(name
: String): nullable ConfigNode do
258 if root
.name
== name
then return root
263 private fun leaves
: Array[ConfigNode] do
264 var res
= new Array[ConfigNode]
265 var todo
= new Array[ConfigNode]
267 while not todo
.is_empty
do
269 if node
.children
.is_empty
then
272 todo
.add_all node
.children
.values
279 private class ConfigNode
281 var parent
: nullable ConfigNode = null
282 var children
= new HashMap[String, ConfigNode]
283 var name
: String is writable
284 var value
: nullable String = null
287 if parent
== null then
290 return "{parent.key}.{name}"
293 fun get_child
(name
: String): nullable ConfigNode do
294 if children
.has_key
(name
) then
295 return children
[name
]