Mercurial > wow > reaction
comparison lib/AceDB-3.0/AceDB-3.0.lua @ 28:21bcaf8215ff
- converted to Ace3
- rearranged file layout
- configGUI menus not working right now
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Mon, 17 Mar 2008 18:24:53 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
27:f1e838841ce1 | 28:21bcaf8215ff |
---|---|
1 --[[ $Id: AceDB-3.0.lua 63886 2008-03-08 12:43:31Z nevcairiel $ ]] | |
2 local ACEDB_MAJOR, ACEDB_MINOR = "AceDB-3.0", 5 | |
3 local AceDB, oldminor = LibStub:NewLibrary(ACEDB_MAJOR, ACEDB_MINOR) | |
4 | |
5 if not AceDB then return end -- No upgrade needed | |
6 | |
7 local type = type | |
8 local pairs, next = pairs, next | |
9 local rawget, rawset = rawget, rawset | |
10 local setmetatable = setmetatable | |
11 | |
12 AceDB.db_registry = AceDB.db_registry or {} | |
13 AceDB.frame = AceDB.frame or CreateFrame("Frame") | |
14 | |
15 local CallbackHandler | |
16 local CallbackDummy = { Fire = function() end } | |
17 | |
18 local DBObjectLib = {} | |
19 | |
20 --[[------------------------------------------------------------------------- | |
21 AceDB Utility Functions | |
22 ---------------------------------------------------------------------------]] | |
23 | |
24 -- Simple shallow copy for copying defaults | |
25 local function copyTable(src, dest) | |
26 if type(dest) ~= "table" then dest = {} end | |
27 if type(src) == "table" then | |
28 for k,v in pairs(src) do | |
29 if type(v) == "table" then | |
30 -- try to index the key first so that the metatable creates the defaults, if set, and use that table | |
31 v = copyTable(v, dest[k]) | |
32 end | |
33 dest[k] = v | |
34 end | |
35 end | |
36 return dest | |
37 end | |
38 | |
39 -- Called to add defaults to a section of the database | |
40 -- | |
41 -- When a ["*"] default section is indexed with a new key, a table is returned | |
42 -- and set in the host table. These tables must be cleaned up by removeDefaults | |
43 -- in order to ensure we don't write empty default tables. | |
44 local function copyDefaults(dest, src) | |
45 -- this happens if some value in the SV overwrites our default value with a non-table | |
46 --if type(dest) ~= "table" then return end | |
47 for k, v in pairs(src) do | |
48 if k == "*" or k == "**" then | |
49 if type(v) == "table" then | |
50 -- This is a metatable used for table defaults | |
51 local mt = { | |
52 -- This handles the lookup and creation of new subtables | |
53 __index = function(t,k) | |
54 if k == nil then return nil end | |
55 local tbl = {} | |
56 copyDefaults(tbl, v) | |
57 rawset(t, k, tbl) | |
58 return tbl | |
59 end, | |
60 } | |
61 setmetatable(dest, mt) | |
62 -- handle already existing tables in the SV | |
63 for dk, dv in pairs(dest) do | |
64 if not rawget(src, dk) and type(dv) == "table" then | |
65 copyDefaults(dv, v) | |
66 end | |
67 end | |
68 else | |
69 -- Values are not tables, so this is just a simple return | |
70 local mt = {__index = function(t,k) return k~=nil and v or nil end} | |
71 setmetatable(dest, mt) | |
72 end | |
73 elseif type(v) == "table" then | |
74 if not rawget(dest, k) then rawset(dest, k, {}) end | |
75 if type(dest[k]) == "table" then | |
76 copyDefaults(dest[k], v) | |
77 if src['**'] then | |
78 copyDefaults(dest[k], src['**']) | |
79 end | |
80 end | |
81 else | |
82 if rawget(dest, k) == nil then | |
83 rawset(dest, k, v) | |
84 end | |
85 end | |
86 end | |
87 end | |
88 | |
89 -- Called to remove all defaults in the default table from the database | |
90 local function removeDefaults(db, defaults, blocker) | |
91 for k,v in pairs(defaults) do | |
92 if k == "*" or k == "**" then | |
93 if type(v) == "table" then | |
94 -- Loop through all the actual k,v pairs and remove | |
95 for key, value in pairs(db) do | |
96 if type(value) == "table" then | |
97 -- if the key was not explicitly specified in the defaults table, just strip everything from * and ** tables | |
98 if defaults[key] == nil then | |
99 removeDefaults(value, v) | |
100 -- if the table is empty afterwards, remove it | |
101 if not next(value) then | |
102 db[key] = nil | |
103 end | |
104 -- if it was specified, only strip ** content, but block values which were set in the key table | |
105 elseif k == "**" then | |
106 removeDefaults(value, v, defaults[key]) | |
107 end | |
108 end | |
109 end | |
110 elseif k == "*" then | |
111 -- check for non-table default | |
112 for key, value in pairs(db) do | |
113 if defaults[key] == nil and v == value then | |
114 db[key] = nil | |
115 end | |
116 end | |
117 end | |
118 elseif type(v) == "table" and type(db[k]) == "table" then | |
119 -- if a blocker was set, dive into it, to allow multi-level defaults | |
120 removeDefaults(db[k], v, blocker and blocker[k]) | |
121 if not next(db[k]) then | |
122 db[k] = nil | |
123 end | |
124 else | |
125 -- check if the current value matches the default, and that its not blocked by another defaults table | |
126 if db[k] == defaults[k] and (not blocker or blocker[k] == nil) then | |
127 db[k] = nil | |
128 end | |
129 end | |
130 end | |
131 -- remove all metatables from the db | |
132 setmetatable(db, nil) | |
133 end | |
134 | |
135 -- This is called when a table section is first accessed, to set up the defaults | |
136 local function initSection(db, section, svstore, key, defaults) | |
137 local sv = rawget(db, "sv") | |
138 | |
139 local tableCreated | |
140 if not sv[svstore] then sv[svstore] = {} end | |
141 if not sv[svstore][key] then | |
142 sv[svstore][key] = {} | |
143 tableCreated = true | |
144 end | |
145 | |
146 local tbl = sv[svstore][key] | |
147 | |
148 if defaults then | |
149 copyDefaults(tbl, defaults) | |
150 end | |
151 rawset(db, section, tbl) | |
152 | |
153 return tableCreated, tbl | |
154 end | |
155 | |
156 -- Metatable to handle the dynamic creation of sections and copying of sections. | |
157 local dbmt = { | |
158 __index = function(t, section) | |
159 local keys = rawget(t, "keys") | |
160 local key = keys[section] | |
161 if key then | |
162 local defaultTbl = rawget(t, "defaults") | |
163 local defaults = defaultTbl and defaultTbl[section] | |
164 | |
165 if section == "profile" then | |
166 local new = initSection(t, section, "profiles", key, defaults) | |
167 if new then | |
168 -- Callback: OnNewProfile, database, newProfileKey | |
169 t.callbacks:Fire("OnNewProfile", t, key) | |
170 end | |
171 elseif section == "profiles" then | |
172 local sv = rawget(t, "sv") | |
173 if not sv.profiles then sv.profiles = {} end | |
174 rawset(t, "profiles", sv.profiles) | |
175 elseif section == "global" then | |
176 local sv = rawget(t, "sv") | |
177 if not sv.global then sv.global = {} end | |
178 if defaults then | |
179 copyDefaults(sv.global, defaults) | |
180 end | |
181 rawset(t, section, sv.global) | |
182 else | |
183 initSection(t, section, section, key, defaults) | |
184 end | |
185 end | |
186 | |
187 return rawget(t, section) | |
188 end | |
189 } | |
190 | |
191 local function validateDefaults(defaults, keyTbl, offset) | |
192 if not defaults then return end | |
193 offset = offset or 0 | |
194 for k in pairs(defaults) do | |
195 if not keyTbl[k] or k == "profiles" then | |
196 error(("Usage: AceDBObject:RegisterDefaults(defaults): '%s' is not a valid datatype."):format(k), 3 + offset) | |
197 end | |
198 end | |
199 end | |
200 | |
201 local preserve_keys = { | |
202 ["callbacks"] = true, | |
203 ["RegisterCallback"] = true, | |
204 ["UnregisterCallback"] = true, | |
205 ["UnregisterAllCallbacks"] = true, | |
206 ["children"] = true, | |
207 } | |
208 | |
209 local realmKey = GetRealmName() | |
210 local charKey = UnitName("player") .. " - " .. realmKey | |
211 local _, classKey = UnitClass("player") | |
212 local _, raceKey = UnitRace("player") | |
213 local factionKey = UnitFactionGroup("player") | |
214 local factionrealmKey = factionKey .. " - " .. realmKey | |
215 -- Actual database initialization function | |
216 local function initdb(sv, defaults, defaultProfile, olddb, parent) | |
217 -- Generate the database keys for each section | |
218 | |
219 -- Make a container for profile keys | |
220 if not sv.profileKeys then sv.profileKeys = {} end | |
221 | |
222 -- Try to get the profile selected from the char db | |
223 local profileKey = sv.profileKeys[charKey] or defaultProfile or charKey | |
224 sv.profileKeys[charKey] = profileKey | |
225 | |
226 -- This table contains keys that enable the dynamic creation | |
227 -- of each section of the table. The 'global' and 'profiles' | |
228 -- have a key of true, since they are handled in a special case | |
229 local keyTbl= { | |
230 ["char"] = charKey, | |
231 ["realm"] = realmKey, | |
232 ["class"] = classKey, | |
233 ["race"] = raceKey, | |
234 ["faction"] = factionKey, | |
235 ["factionrealm"] = factionrealmKey, | |
236 ["profile"] = profileKey, | |
237 ["global"] = true, | |
238 ["profiles"] = true, | |
239 } | |
240 | |
241 validateDefaults(defaults, keyTbl, 1) | |
242 | |
243 -- This allows us to use this function to reset an entire database | |
244 -- Clear out the old database | |
245 if olddb then | |
246 for k,v in pairs(olddb) do if not preserve_keys[k] then olddb[k] = nil end end | |
247 end | |
248 | |
249 -- Give this database the metatable so it initializes dynamically | |
250 local db = setmetatable(olddb or {}, dbmt) | |
251 | |
252 if not rawget(db, "callbacks") then | |
253 -- try to load CallbackHandler-1.0 if it loaded after our library | |
254 if not CallbackHandler then CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0", true) end | |
255 db.callbacks = CallbackHandler and CallbackHandler:New(db) or CallbackDummy | |
256 end | |
257 | |
258 -- Copy methods locally into the database object, to avoid hitting | |
259 -- the metatable when calling methods | |
260 | |
261 if not parent then | |
262 for name, func in pairs(DBObjectLib) do | |
263 db[name] = func | |
264 end | |
265 else | |
266 -- hack this one in | |
267 db.RegisterDefaults = DBObjectLib.RegisterDefaults | |
268 end | |
269 | |
270 -- Set some properties in the database object | |
271 db.profiles = sv.profiles | |
272 db.keys = keyTbl | |
273 db.sv = sv | |
274 --db.sv_name = name | |
275 db.defaults = defaults | |
276 db.parent = parent | |
277 | |
278 -- store the DB in the registry | |
279 AceDB.db_registry[db] = true | |
280 | |
281 return db | |
282 end | |
283 | |
284 -- handle PLAYER_LOGOUT | |
285 -- strip all defaults from all databases | |
286 local function logoutHandler(frame, event) | |
287 if event == "PLAYER_LOGOUT" then | |
288 for db in pairs(AceDB.db_registry) do | |
289 db.callbacks:Fire("OnDatabaseShutdown", db) | |
290 for section, key in pairs(db.keys) do | |
291 if db.defaults and db.defaults[section] and rawget(db, section) then | |
292 removeDefaults(db[section], db.defaults[section]) | |
293 end | |
294 end | |
295 end | |
296 end | |
297 end | |
298 | |
299 AceDB.frame:RegisterEvent("PLAYER_LOGOUT") | |
300 AceDB.frame:SetScript("OnEvent", logoutHandler) | |
301 | |
302 | |
303 --[[------------------------------------------------------------------------- | |
304 AceDB Object Method Definitions | |
305 ---------------------------------------------------------------------------]] | |
306 | |
307 -- DBObject:RegisterDefaults(defaults) | |
308 -- defaults (table) - A table of defaults for this database | |
309 -- | |
310 -- Sets the defaults table for the given database object by clearing any | |
311 -- that are currently set, and then setting the new defaults. | |
312 function DBObjectLib:RegisterDefaults(defaults) | |
313 if defaults and type(defaults) ~= "table" then | |
314 error("Usage: AceDBObject:RegisterDefaults(defaults): 'defaults' - table or nil expected.", 2) | |
315 end | |
316 | |
317 validateDefaults(defaults, self.keys) | |
318 | |
319 -- Remove any currently set defaults | |
320 if self.defaults then | |
321 for section,key in pairs(self.keys) do | |
322 if self.defaults[section] and rawget(self, section) then | |
323 removeDefaults(self[section], self.defaults[section]) | |
324 end | |
325 end | |
326 end | |
327 | |
328 -- Set the DBObject.defaults table | |
329 self.defaults = defaults | |
330 | |
331 -- Copy in any defaults, only touching those sections already created | |
332 if defaults then | |
333 for section,key in pairs(self.keys) do | |
334 if defaults[section] and rawget(self, section) then | |
335 copyDefaults(self[section], defaults[section]) | |
336 end | |
337 end | |
338 end | |
339 end | |
340 | |
341 -- DBObject:SetProfile(name) | |
342 -- name (string) - The name of the profile to set as the current profile | |
343 -- | |
344 -- Changes the profile of the database and all of it's namespaces to the | |
345 -- supplied named profile | |
346 function DBObjectLib:SetProfile(name) | |
347 if type(name) ~= "string" then | |
348 error("Usage: AceDBObject:SetProfile(name): 'name' - string expected.", 2) | |
349 end | |
350 | |
351 -- changing to the same profile, dont do anything | |
352 if name == self.keys.profile then return end | |
353 | |
354 local oldProfile = self.profile | |
355 local defaults = self.defaults and self.defaults.profile | |
356 | |
357 if oldProfile and defaults then | |
358 -- Remove the defaults from the old profile | |
359 removeDefaults(oldProfile, defaults) | |
360 end | |
361 | |
362 self.profile = nil | |
363 self.keys["profile"] = name | |
364 self.sv.profileKeys[charKey] = name | |
365 | |
366 -- Callback: OnProfileChanged, database, newProfileKey | |
367 self.callbacks:Fire("OnProfileChanged", self, name) | |
368 | |
369 -- populate to child namespaces | |
370 if self.children then | |
371 for _, db in pairs(self.children) do | |
372 DBObjectLib.SetProfile(db, name) | |
373 end | |
374 end | |
375 end | |
376 | |
377 -- DBObject:GetProfiles(tbl) | |
378 -- tbl (table) - A table to store the profile names in (optional) | |
379 -- | |
380 -- Returns a table with the names of the existing profiles in the database. | |
381 -- You can optionally supply a table to re-use for this purpose. | |
382 function DBObjectLib:GetProfiles(tbl) | |
383 if tbl and type(tbl) ~= "table" then | |
384 error("Usage: AceDBObject:GetProfiles(tbl): 'tbl' - table or nil expected.", 2) | |
385 end | |
386 | |
387 -- Clear the container table | |
388 if tbl then | |
389 for k,v in pairs(tbl) do tbl[k] = nil end | |
390 else | |
391 tbl = {} | |
392 end | |
393 | |
394 local curProfile = self.keys.profile | |
395 | |
396 local i = 0 | |
397 for profileKey in pairs(self.profiles) do | |
398 i = i + 1 | |
399 tbl[i] = profileKey | |
400 if curProfile and profileKey == curProfile then curProfile = nil end | |
401 end | |
402 | |
403 -- Add the current profile, if it hasn't been created yet | |
404 if curProfile then | |
405 i = i + 1 | |
406 tbl[i] = curProfile | |
407 end | |
408 | |
409 return tbl, i | |
410 end | |
411 | |
412 -- DBObject:GetCurrentProfile() | |
413 -- | |
414 -- Returns the current profile name used by the database | |
415 function DBObjectLib:GetCurrentProfile() | |
416 return self.keys.profile | |
417 end | |
418 | |
419 -- DBObject:DeleteProfile(name) | |
420 -- name (string) - The name of the profile to be deleted | |
421 -- | |
422 -- Deletes a named profile. This profile must not be the active profile. | |
423 function DBObjectLib:DeleteProfile(name, silent) | |
424 if type(name) ~= "string" then | |
425 error("Usage: AceDBObject:DeleteProfile(name): 'name' - string expected.", 2) | |
426 end | |
427 | |
428 if self.keys.profile == name then | |
429 error("Cannot delete the active profile in an AceDBObject.", 2) | |
430 end | |
431 | |
432 if not rawget(self.sv.profiles, name) and not silent then | |
433 error("Cannot delete profile '" .. name .. "'. It does not exist.", 2) | |
434 end | |
435 | |
436 self.sv.profiles[name] = nil | |
437 -- Callback: OnProfileDeleted, database, profileKey | |
438 self.callbacks:Fire("OnProfileDeleted", self, name) | |
439 | |
440 -- populate to child namespaces | |
441 if self.children then | |
442 for _, db in pairs(self.children) do | |
443 DBObjectLib.DeleteProfile(db, name, true) | |
444 end | |
445 end | |
446 end | |
447 | |
448 -- DBObject:CopyProfile(name) | |
449 -- name (string) - The name of the profile to be copied into the current profile | |
450 -- | |
451 -- Copies a named profile into the current profile, overwriting any conflicting | |
452 -- settings. | |
453 function DBObjectLib:CopyProfile(name, silent) | |
454 if type(name) ~= "string" then | |
455 error("Usage: AceDBObject:CopyProfile(name): 'name' - string expected.", 2) | |
456 end | |
457 | |
458 if name == self.keys.profile then | |
459 error("Cannot have the same source and destination profiles.", 2) | |
460 end | |
461 | |
462 if not rawget(self.sv.profiles, name) and not silent then | |
463 error("Cannot copy profile '" .. name .. "'. It does not exist.", 2) | |
464 end | |
465 | |
466 -- Reset the profile before copying | |
467 DBObjectLib.ResetProfile(self) | |
468 | |
469 local profile = self.profile | |
470 local source = self.sv.profiles[name] | |
471 | |
472 copyTable(source, profile) | |
473 | |
474 -- Callback: OnProfileCopied, database, sourceProfileKey | |
475 self.callbacks:Fire("OnProfileCopied", self, name) | |
476 | |
477 -- populate to child namespaces | |
478 if self.children then | |
479 for _, db in pairs(self.children) do | |
480 DBObjectLib.CopyProfile(db, name, true) | |
481 end | |
482 end | |
483 end | |
484 | |
485 -- DBObject:ResetProfile() | |
486 -- | |
487 -- Resets the current profile | |
488 function DBObjectLib:ResetProfile() | |
489 local profile = self.profile | |
490 | |
491 for k,v in pairs(profile) do | |
492 profile[k] = nil | |
493 end | |
494 | |
495 local defaults = self.defaults and self.defaults.profile | |
496 if defaults then | |
497 copyDefaults(profile, defaults) | |
498 end | |
499 | |
500 -- Callback: OnProfileReset, database | |
501 self.callbacks:Fire("OnProfileReset", self) | |
502 | |
503 -- populate to child namespaces | |
504 if self.children then | |
505 for _, db in pairs(self.children) do | |
506 DBObjectLib.ResetProfile(db) | |
507 end | |
508 end | |
509 end | |
510 | |
511 -- DBObject:ResetDB(defaultProfile) | |
512 -- defaultProfile (string) - The profile name to use as the default | |
513 -- | |
514 -- Resets the entire database, using the string defaultProfile as the default | |
515 -- profile. | |
516 function DBObjectLib:ResetDB(defaultProfile) | |
517 if defaultProfile and type(defaultProfile) ~= "string" then | |
518 error("Usage: AceDBObject:ResetDB(defaultProfile): 'defaultProfile' - string or nil expected.", 2) | |
519 end | |
520 | |
521 local sv = self.sv | |
522 for k,v in pairs(sv) do | |
523 sv[k] = nil | |
524 end | |
525 | |
526 local parent = self.parent | |
527 | |
528 initdb(sv, self.defaults, defaultProfile, self) | |
529 | |
530 -- Callback: OnDatabaseReset, database | |
531 self.callbacks:Fire("OnDatabaseReset", self) | |
532 -- Callback: OnProfileChanged, database, profileKey | |
533 self.callbacks:Fire("OnProfileChanged", self, self.keys["profile"]) | |
534 | |
535 -- fix the child namespaces | |
536 if self.children then | |
537 if not sv.namespaces then sv.namespaces = {} end | |
538 for name, db in pairs(self.children) do | |
539 if not sv.namespaces[name] then sv.namespaces[name] = {} end | |
540 initdb(sv.namespaces[name], db.defaults, self.keys.profile, db, self) | |
541 end | |
542 end | |
543 | |
544 return self | |
545 end | |
546 | |
547 -- DBObject:RegisterNamespace(name [, defaults]) | |
548 -- name (string) - The name of the new namespace | |
549 -- defaults (table) - A table of values to use as defaults | |
550 -- | |
551 -- Creates a new database namespace, directly tied to the database. This | |
552 -- is a full scale database in it's own rights other than the fact that | |
553 -- it cannot control its profile individually | |
554 function DBObjectLib:RegisterNamespace(name, defaults) | |
555 if type(name) ~= "string" then | |
556 error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - string expected.", 2) | |
557 end | |
558 if defaults and type(defaults) ~= "table" then | |
559 error("Usage: AceDBObject:RegisterNamespace(name, defaults): 'defaults' - table or nil expected.", 2) | |
560 end | |
561 if self.children and self.children[name] then | |
562 error ("Usage: AceDBObject:RegisterNamespace(name, defaults): 'name' - a namespace with that name already exists.", 2) | |
563 end | |
564 | |
565 local sv = self.sv | |
566 if not sv.namespaces then sv.namespaces = {} end | |
567 if not sv.namespaces[name] then | |
568 sv.namespaces[name] = {} | |
569 end | |
570 | |
571 local newDB = initdb(sv.namespaces[name], defaults, self.keys.profile, nil, self) | |
572 | |
573 if not self.children then self.children = {} end | |
574 self.children[name] = newDB | |
575 return newDB | |
576 end | |
577 | |
578 --[[------------------------------------------------------------------------- | |
579 AceDB Exposed Methods | |
580 ---------------------------------------------------------------------------]] | |
581 | |
582 -- AceDB:New(name, defaults, defaultProfile) | |
583 -- name (table or string) - The name of variable, or table to use for the database | |
584 -- defaults (table) - A table of database defaults | |
585 -- defaultProfile (string) - The name of the default profile | |
586 -- | |
587 -- Creates a new database object that can be used to handle database settings | |
588 -- and profiles. | |
589 function AceDB:New(tbl, defaults, defaultProfile) | |
590 if type(tbl) == "string" then | |
591 local name = tbl | |
592 tbl = getglobal(name) | |
593 if not tbl then | |
594 tbl = {} | |
595 setglobal(name, tbl) | |
596 end | |
597 end | |
598 | |
599 if type(tbl) ~= "table" then | |
600 error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'tbl' - table expected.", 2) | |
601 end | |
602 | |
603 if defaults and type(defaults) ~= "table" then | |
604 error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaults' - table expected.", 2) | |
605 end | |
606 | |
607 if defaultProfile and type(defaultProfile) ~= "string" then | |
608 error("Usage: AceDB:New(tbl, defaults, defaultProfile): 'defaultProfile' - string expected.", 2) | |
609 end | |
610 | |
611 return initdb(tbl, defaults, defaultProfile) | |
612 end | |
613 | |
614 -- upgrade existing databases | |
615 for db in pairs(AceDB.db_registry) do | |
616 if not db.parent then | |
617 for name,func in pairs(DBObjectLib) do | |
618 db[name] = func | |
619 end | |
620 else | |
621 db.RegisterDefaults = DBObjectLib.RegisterDefaults | |
622 end | |
623 end |