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