· 9 years ago · Oct 21, 2016, 09:10 PM
1--[[
2Author: TheOriginalBIT
3Version: 2.1
4Created: 9 March 2013
5Last Update: 22 September 2013
6License:
7COPYRIGHT NOTICE
8Copyright © 2013 Joshua Asbury a.k.a TheOriginalBIT [theoriginalbit@gmail.com]
9Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
10associated documentation files (the "Software"), to deal in the Software without restriction,
11including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
12copies of the Software, and to permit persons to whom the Software is furnished to do so,
13subject to the following conditions:
14- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15- Visible credit is given to the original author.
16- The software is distributed in a non-profit way.
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21]]--
22
23local function startsWith(str, prefix)
24 return str:sub( 1, #tostring(prefix)) == tostring(prefix)
25end
26
27local function contains(str, seq)
28 return (str:find(seq, 1)) ~= nil
29end
30
31local function split(str, patt)
32 local t = {}
33
34 for s in str:gmatch("[^"..patt.."]+") do
35 t[#t+1] = s
36 end
37
38 return t
39end
40
41local function trim(str)
42 return (str:gsub("^%s*(.-)%s*$", "%1"))
43end
44
45local function log2(n)
46 return math.floor(math.log(n) / math.log(2))
47end
48
49local function assert( _condition, _message, _level )
50 _level = tonumber( _level ) or 1
51 _level = _level == 0 and 0 or (_level + 1)
52 if not _condition then
53 error( _message or "assertion failed!", _level )
54 end
55 return _condition
56end
57
58local Configuration = {}
59
60Configuration.__index = Configuration
61
62local function validateParam(param, value)
63 return type(param) == value
64end
65
66local function validateBoolean(b)
67 return (b == "true" or b == "false" or b == "1" or b == "0" or b == 1 or b == 0 or b == true or b == false or b == "yes" or b == "no")
68end
69
70local function buildPattern(word)
71 local result = ""
72 for ixLetter = 1, #word do
73 result = string.format("%s[%s%s]", result, string.lower(string.sub(word, ixLetter, ixLetter)), string.upper(string.sub(word, ixLetter, ixLetter)))
74 if ixLetter < #word then result = result .. "%s*" end
75 end
76 return result
77end
78
79local function parseColor(source)
80 source = source:gsub("colou?rs\.", "")
81 for key, value in pairs(colors) do source = source:gsub(buildPattern(key), tostring(value)) end
82 for key, value in pairs(colours) do source = source:gsub(buildPattern(key), tostring(value)) end
83 return tonumber(source)
84end
85
86local function validateColor(col)
87 if tonumber(col) then
88 -- convert it down to incremental numbering aiming to see 0-15 here
89 local shifted = log2(tonumber(col))
90 -- make sure it is 0-15
91 if shifted%1 == 0 and shifted >= 0 and shifted <= 15 then
92 return true, col
93 end
94 elseif type(col) == "string" then
95 col = parseColor(col)
96 if col then
97 return true, col
98 end
99 end
100
101 return false
102end
103
104function new(path, name)
105 assert(validateParam(path, "string"), "Invalid parameter #1: Expected string, got "..type(path), 2)
106 assert(validateParam(name, "string"), "Invalid parameter #2: Expected string, got "..type(name), 2)
107
108 return setmetatable({name = name:upper(), path = path, properties = {}}, Configuration)
109end
110
111function init(path, name)
112 assert(validateParam(path, "string"), "Invalid parameter #1: Expected string, got "..type(path), 2)
113 assert(validateParam(name, "string"), "Invalid parameter #2: Expected string, got "..type(name), 2)
114
115 return setmetatable({name = name:upper(), path = path, properties = {}}, Configuration)
116end
117
118local function get(self, key, default)
119 if type(self.properties[key]) == "table" then
120 self.properties[key].default = default
121 return self.properties[key].value
122 end
123 --# the key doesn't exist in the table, create it now
124 self.properties[key] = {}
125 self.properties[key].value = default
126 self.properties[key].default = default
127 return default
128end
129
130local function set(self, key, value)
131 assert(self.properties[key], "Invalid parameter #1: The key '"..tostring(key).."' does not exist in the configuration file.", 3)
132
133 self.properties[key].value = value
134end
135
136function Configuration:getBoolean(key, default)
137 --# validate parameters
138 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
139 assert(validateBoolean(default), "Invalid parameter #2: Expected boolean, got "..tostring(default).." of type "..type(default), 2)
140
141 --# get the value from the config
142 local value = get(self, key, default)
143
144 --# validate the config boolean
145 assert(validateBoolean(value), "Configuration error: No boolean value found under the key \""..key.."\", found "..tostring(value).." of type "..type(value), 2)
146
147 --# return a boolean
148 return v == "true" or v == true or v == "1" or v == 1 or v == "yes"
149end
150
151function Configuration:setBoolean(key, value)
152 --# validate parameters
153 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
154 assert(validateBoolean(value), "Invalid parameter #2: Expected boolean, got "..tostring(default).." of type "..type(value), 2)
155
156 --# set the value
157 set(self, key, value)
158end
159
160function Configuration:getNumber(key, default)
161 --# validate parameters
162 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
163 assert(validateParam(default, "number"), "Invalid parameter #2: Expected number, got "..tostring(default).." of type "..type(default), 2)
164
165 --# get the number from the config
166 local value = tonumber(get(self, key, default))
167
168 --# validate the number
169 assert(validateParam(value, "number"), "Configuration error: No number found under the key \""..key.."\", found "..tostring(value).." of type "..type(value), 2)
170
171 --# return the number
172 return value
173end
174
175function Configuration:setNumber(key, value)
176 --# validate parameters
177 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
178 assert(validateParam(value, "number"), "Invalid parameter #2: Expected number, got "..tostring(default).." of type "..type(value), 2)
179
180 --# set the value
181 set(self, key, value)
182end
183
184function Configuration:getString(key, default)
185 --# validate parameters
186 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
187 assert(validateParam(default, "string"), "Invalid parameter #2: Expected string, got "..tostring(default).." of type "..type(default), 2)
188
189 --# get the string from the config
190 local value = get(self, key, default)
191
192 --# return the string
193 return value
194end
195
196function Configuration:setString(key, value)
197 --# validate parameters
198 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
199 assert(validateParam(value, "string"), "Invalid parameter #2: Expected string, got "..tostring(default).." of type "..type(value), 2)
200
201 --# set the value
202 set(self, key, value)
203end
204
205function Configuration:getColor(key, default)
206 --# validate parameters
207 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
208 local ok, value = validateColor(default)
209 assert(ok, "Invalid parameter #2: Expected number/color, got "..tostring(default).." of type "..type(default), 2)
210
211 --# get the colour from the config
212 ok, value = validateColor(get(self, key, default))
213
214 --# validate the colour
215 assert(ok, "Configuration error: No number/color found under the key\""..key.."\", found "..tostring(value).." of type "..type(value), 2)
216
217 --# add the restriction comment
218 self.properties[key].restrict = "A number between 1 and 32768 or the string colors.<color>"
219
220 return value
221end
222
223function Configuration:setColor(key, value)
224 --# validate parameters
225 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
226 local ok, value = validateColor(value)
227 assert(ok, "Invalid parameter #2: Expected number/color, got "..tostring(default).." of type "..type(default), 2)
228
229 --# set the value
230 set(self, key, value)
231end
232
233--# add the non-English (US) function
234Configuration.getColour = Configuration.getColor
235
236function Configuration:containsKey(key)
237 return (self.properties[key] ~= nil)
238end
239
240function Configuration:load()
241 --# make sure the file exists
242 if not fs.exists(self.path) then
243 return false
244 end
245
246 --# read the file
247 local h = fs.open(self.path, 'r')
248 assert(h, "Cannot open configuration file \'"..self.path.."\' for read")
249 local contents = h.readAll()
250 h.close()
251
252 --# make sure there are contents of the file
253 if not contents then
254 return
255 end
256
257 local count = 1
258
259 contents = split(contents, '\n')
260
261 for i = 1, #contents do
262 local v = trim(contents[i])
263 if v and v ~= "" and not startsWith(v, "+--") and contains(v, '=') then
264 local prop = split(v, '=')
265 local key = trim(prop[1])
266 local value = trim(prop[2])
267 self.properties[key] = {}
268 self.properties[key]["value"] = value
269 self.properties[key]["default"] = value
270 elseif v and v ~= "" and not startsWith(v,"+--") and not contains(v, '=') then
271 error("[\""..self.path.."\"] Cannot parse line #"..i.." in configuration file", 0)
272 end
273 end
274 return true
275end
276
277function Configuration:reset(key)
278 assert(type(self) == "table" and type(self.properties) == "table", "Cannot resetting config, have you forgotten to load the config?", 2)
279 assert((key and self.properties[key]) or not key, "No property for key "..tostring(key)..", have you forgotten to load the config?", 2)
280
281 if key then
282 self.properties[key].value = self.properties[key].default
283 else
284 for k,_ in pairs(self.properties) do
285 self.properties[k].value = self.properties[k].default
286 end
287 end
288end
289
290function Configuration:save()
291 --# open the config file
292 local h = fs.open(self.path, 'w')
293
294 --# write the header
295 h.write(string.format("\n+-- %s CONFIGURATION FILE\n+-- Generated by TheOriginalBIT's ccConfig\n\n\n", self.name))
296
297 --# loop through the properties
298 for k,v in pairs(self.properties) do
299 --# write the properties info to file
300 h.write(string.format("+-- %s restrictions: %s; default: %s;\n%s=%s\n\n\n", v.comment or "", v.restrict or "none", tostring(v.default), tostring(k), tostring(v.value)))
301
302 --# flush the file
303 h.flush()
304 end
305
306 --# close the file
307 h.close()
308end
309
310function Configuration:addCommentForKey(key, value)
311 --# validate parameters
312 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
313 assert(self.properties[key], "Invalid parameter #1: No property with key \""..tostring(key).."\" to add comment", 2)
314
315 --# add the comment
316 self.properties[key].comment = value
317end
318
319function Configuration:addRestrictionForKey(key, value)
320 --# validate parameters
321 assert(validateParam(key, "string"), "Invalid parameter #1: Expected string, got "..type(key), 2)
322 assert(self.properties[key], "No property with key \""..tostring(key).."\" to add comment", 2)
323
324 --# add the restriction comment
325 self.properties[key].restrict = value
326end
327
328function Configuration:debug(toFile)
329 toFile = (toFile == true)
330 local h = toFile and fs.open("debug", 'w') or nil
331 for k,v in pairs(self.properties) do
332 if toFile then
333 h.write(k..":"..v.value.."\n")
334 else
335 print(k..":"..v.value)
336 end
337 end
338 if toFile then
339 h.close()
340 end
341end