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
42 if file
.file_exists
then load
45 # Get the config value for `key`
47 # REQUIRE: `has_key(key)`
49 # var config = new ConfigTree("config.ini")
50 # assert config["goo"] == "goo"
51 # assert config["foo.bar"] == "foobar"
52 # assert config["foo.baz"] == "foobaz"
53 fun [](key
: String): String do
54 if not has_key
(key
) then
55 print
"error: config key `{key}` not found"
58 var node
= get_node
(key
).as(not null)
59 if node
.value
== null then
60 print
"error: config key `{key}` has no value"
63 return node
.value
.as(not null)
66 # Get the config values under `key`
68 # REQUIRE: `has_key(key)`
70 # var config = new ConfigTree("config.ini")
71 # var values = config.at("foo")
72 # assert values.has_key("bar")
73 # assert values.has_key("baz")
74 # assert not values.has_key("goo")
75 fun at
(key
: String): Map[String, String] do
76 if not has_key
(key
) then
77 print
"error: config key `{key}` not found"
80 var map
= new HashMap[String, String]
81 var node
= get_node
(key
).as(not null)
82 for k
, child
in node
.children
do
83 if child
.value
== null then continue
84 map
[k
] = child
.value
.to_s
89 # Set `value` at `key`
91 # var config = new ConfigTree("config.ini")
92 # assert config["foo.bar"] == "foobar"
93 # config["foo.bar"] = "baz"
94 # assert config["foo.bar"] == "baz"
95 fun []=(key
: String, value
: nullable String) do
99 # Is `key` in the config?
101 # var config = new ConfigTree("config.ini")
102 # assert config.has_key("goo")
103 # assert config.has_key("foo.bar")
104 # assert not config.has_key("zoo")
105 fun has_key
(key
: String): Bool do
107 var parts
= key
.split
(".").reversed
108 var node
= get_root
(parts
.pop
)
109 if node
== null then return false
110 while not parts
.is_empty
do
111 node
= node
.get_child
(parts
.pop
)
112 if node
== null then return false
117 # Get `self` as a Map of `key`, `value`
119 # var config = new ConfigTree("config.ini")
120 # var map = config.to_map
121 # assert map.has_key("goo")
122 # assert map.has_key("foo.bar")
123 # assert map.has_key("foo.baz")
124 # assert map.length == 3
125 fun to_map
: Map[String, String] do
126 var map
= new HashMap[String, String]
127 for node
in leaves
do
128 if node
.value
== null then continue
129 map
[node
.key
] = node
.value
.to_s
134 redef fun to_s
do return to_map
.join
(", ", ":")
136 redef fun write_to
(stream
) do
137 for node
in leaves
do
138 if node
.value
== null then continue
139 stream
.write
("{node.key}={node.value.to_s}\n")
143 # Reload config from file
144 # Done automatically at init
146 # Example with hierarchical ini file:
153 # str.write_to_file("config1.ini")
155 # var config = new ConfigTree("config1.ini")
156 # assert config["foo.bar"] == "foobar"
158 # Example with sections:
168 # str.write_to_file("config2.ini")
170 # config = new ConfigTree("config2.ini")
171 # assert config["foo.bar"] == "foobar"
172 # assert config["boo.bar"] == "boobar"
174 # Example with both hierarchy and section:
183 # baz.bar=gooboobazbar"""
184 # str.write_to_file("config3.ini")
186 # config = new ConfigTree("config3.ini")
187 # assert config["goo"] == "goo"
188 # assert config["foo.bar.baz"] == "foobarbaz"
189 # assert config["goo.boo.bar"] == "gooboobar"
190 # assert config["goo.boo.baz.bar"] == "gooboobazbar"
193 var stream
= new IFStream.open
(ini_file
)
194 var path
: nullable String = null
195 while not stream
.eof
do
196 var line
= stream
.read_line
197 if line
.is_empty
then
199 else if line
.has_prefix
(";") then
201 else if line
.has_prefix
("[") then
203 var key
= line
.substring
(1, line
.length
- 2)
207 var parts
= line
.split
("=")
208 var key
= parts
[0].trim
209 var val
= parts
[1].trim
213 set_node
("{path}.{key}", val
)
220 # Save config to file
221 fun save
do write_to_file
(ini_file
)
223 private var roots
= new Array[ConfigNode]
225 private fun set_node
(key
: String, value
: nullable String) do
227 var parts
= key
.split
(".").reversed
229 var root
= get_root
(k
)
231 root
= new ConfigNode(k
)
232 if parts
.is_empty
then
237 while not parts
.is_empty
do
239 var node
= root
.get_child
(k
)
241 node
= new ConfigNode(k
)
243 root
.children
[node
.name
] = node
245 if parts
.is_empty
then
252 private fun get_node
(key
: String): nullable ConfigNode do
254 var parts
= key
.split
(".").reversed
255 var node
= get_root
(parts
.pop
)
256 while not parts
.is_empty
do
257 node
= node
.get_child
(parts
.pop
)
262 private fun get_root
(name
: String): nullable ConfigNode do
264 if root
.name
== name
then return root
269 private fun leaves
: Array[ConfigNode] do
270 var res
= new Array[ConfigNode]
271 var todo
= new Array[ConfigNode]
273 while not todo
.is_empty
do
275 if node
.children
.is_empty
then
278 todo
.add_all node
.children
.values
285 private class ConfigNode
286 var parent
: nullable ConfigNode
287 var children
= new HashMap[String, ConfigNode]
288 var name
: String is writable
289 var value
: nullable String
291 init(name
: String) do
296 if parent
== null then
299 return "{parent.key}.{name}"
302 fun get_child
(name
: String): nullable ConfigNode do
303 if children
.has_key
(name
) then
304 return children
[name
]