359a2ba6f8ac068125e10370c859656c5c65819e
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 # var config = new ConfigTree("config.ini")
45 # assert config["goo"] == "goo"
46 # assert config["foo.bar"] == "foobar"
47 # assert config["foo.baz"] == "foobaz"
48 # assert config["fail.fail"] == null
49 fun [](key
: String): nullable String do
50 var node
= get_node
(key
)
51 if node
== null then return null
55 # Get the config values under `key`
57 # var config = new ConfigTree("config.ini")
58 # var values = config.at("foo")
59 # assert values.has_key("bar")
60 # assert values.has_key("baz")
61 # assert not values.has_key("goo")
63 # Return null if the key does not exists.
65 # assert config.at("fail.fail") == null
66 fun at
(key
: String): nullable Map[String, String] do
67 var node
= get_node
(key
)
68 if node
== null then return null
69 var map
= new HashMap[String, String]
70 for k
, child
in node
.children
do
71 var value
= child
.value
72 if value
== null then continue
78 # Set `value` at `key`
80 # var config = new ConfigTree("config.ini")
81 # assert config["foo.bar"] == "foobar"
82 # config["foo.bar"] = "baz"
83 # assert config["foo.bar"] == "baz"
84 fun []=(key
: String, value
: nullable String) do
88 # Is `key` in the config?
90 # var config = new ConfigTree("config.ini")
91 # assert config.has_key("goo")
92 # assert config.has_key("foo.bar")
93 # assert not config.has_key("zoo")
94 fun has_key
(key
: String): Bool do
95 var parts
= key
.split
(".").reversed
96 var node
= get_root
(parts
.pop
)
97 if node
== null then return false
98 while not parts
.is_empty
do
99 node
= node
.get_child
(parts
.pop
)
100 if node
== null then return false
105 # Get `self` as a Map of `key`, `value`
107 # var config = new ConfigTree("config.ini")
108 # var map = config.to_map
109 # assert map.has_key("goo")
110 # assert map.has_key("foo.bar")
111 # assert map.has_key("foo.baz")
112 # assert map.length == 3
113 fun to_map
: Map[String, String] do
114 var map
= new HashMap[String, String]
115 for node
in leaves
do
116 var value
= node
.value
117 if value
== null then continue
118 map
[node
.key
] = value
123 redef fun to_s
do return to_map
.join
(", ", ":")
125 redef fun write_to
(stream
) do
126 for node
in leaves
do
127 if node
.value
== null then continue
128 stream
.write
("{node.key}={node.value.to_s}\n")
132 # Reload config from file
133 # Done automatically at init
135 # Example with hierarchical ini file:
142 # str.write_to_file("config1.ini")
144 # var config = new ConfigTree("config1.ini")
145 # assert config["foo.bar"] == "foobar"
147 # Example with sections:
157 # str.write_to_file("config2.ini")
159 # config = new ConfigTree("config2.ini")
160 # assert config["foo.bar"] == "foobar"
161 # assert config["boo.bar"] == "boobar"
163 # Example with both hierarchy and section:
172 # baz.bar=gooboobazbar"""
173 # str.write_to_file("config3.ini")
175 # config = new ConfigTree("config3.ini")
176 # assert config["goo"] == "goo"
177 # assert config["foo.bar.baz"] == "foobarbaz"
178 # assert config["goo.boo.bar"] == "gooboobar"
179 # assert config["goo.boo.baz.bar"] == "gooboobazbar"
181 # Using the array notation
187 # str.write_to_file("config4.ini")
189 # config = new ConfigTree("config4.ini")
190 # print config.to_map.join(":", ",")
191 # assert config["foo.0"] == "a"
192 # assert config["foo.1"] == "b"
193 # assert config["foo.2"] == "c"
194 # assert config.at("foo").values.join(",") == "a,b,c"
197 var stream
= new FileReader.open
(ini_file
)
198 var path
: nullable String = null
200 while not stream
.eof
do
201 var line
= stream
.read_line
203 if line
.is_empty
then
205 else if line
.has_prefix
(";") then
207 else if line
.has_prefix
("[") then
209 var key
= line
.substring
(1, line
.length
- 2)
213 var parts
= line
.split_once_on
("=")
214 if parts
.length
== 1 then
217 var key
= parts
[0].trim
218 var val
= parts
[1].trim
219 if path
!= null then key
= "{path}.{key}"
220 if key
.has_suffix
("[]") then
230 # Save config to file
231 fun save
do write_to_file
(ini_file
)
233 private var roots
= new Array[ConfigNode]
235 # Append `value` to array at `key`
236 private fun set_array
(key
: String, value
: nullable String) do
237 key
= key
.substring
(0, key
.length
- 2)
239 var node
= get_node
(key
)
240 if node
!= null then len
= node
.children
.length
241 set_node
("{key}.{len.to_s}", value
)
244 private fun set_node
(key
: String, value
: nullable String) do
245 var parts
= key
.split
(".").reversed
247 var root
= get_root
(k
)
249 root
= new ConfigNode(k
)
250 if parts
.is_empty
then
255 while not parts
.is_empty
do
257 var node
= root
.get_child
(k
)
259 node
= new ConfigNode(k
)
261 root
.children
[node
.name
] = node
263 if parts
.is_empty
then
270 private fun get_node
(key
: String): nullable ConfigNode do
271 var parts
= key
.split
(".").reversed
272 var node
= get_root
(parts
.pop
)
273 while node
!= null and not parts
.is_empty
do
274 node
= node
.get_child
(parts
.pop
)
279 private fun get_root
(name
: String): nullable ConfigNode do
281 if root
.name
== name
then return root
286 private fun leaves
: Array[ConfigNode] do
287 var res
= new Array[ConfigNode]
288 var todo
= new Array[ConfigNode]
290 while not todo
.is_empty
do
292 if node
.children
.is_empty
then
295 todo
.add_all node
.children
.values
302 private class ConfigNode
304 var parent
: nullable ConfigNode = null
305 var children
= new HashMap[String, ConfigNode]
306 var name
: String is writable
307 var value
: nullable String = null
310 var parent
= self.parent
311 if parent
== null then
314 return "{parent.key}.{name}"
317 fun get_child
(name
: String): nullable ConfigNode do
318 if children
.has_key
(name
) then
319 return children
[name
]