Mercurial > wow > reaction
comparison modules/FuBar_ReActionFu/lib/AceLibrary/AceLibrary.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 --[[ | |
2 Name: AceLibrary | |
3 Revision: $Rev: 49421 $ | |
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) | |
5 Inspired By: Iriel (iriel@vigilance-committee.org) | |
6 Tekkub (tekkub@gmail.com) | |
7 Revision: $Rev: 49421 $ | |
8 Website: http://www.wowace.com/ | |
9 Documentation: http://www.wowace.com/index.php/AceLibrary | |
10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary | |
11 Description: Versioning library to handle other library instances, upgrading, | |
12 and proper access. | |
13 It also provides a base for libraries to work off of, providing | |
14 proper error tools. It is handy because all the errors occur in the | |
15 file that called it, not in the library file itself. | |
16 Dependencies: None | |
17 License: LGPL v2.1 | |
18 ]] | |
19 | |
20 local ACELIBRARY_MAJOR = "AceLibrary" | |
21 local ACELIBRARY_MINOR = "$Revision: 49421 $" | |
22 | |
23 local _G = getfenv(0) | |
24 local previous = _G[ACELIBRARY_MAJOR] | |
25 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end | |
26 | |
27 do | |
28 -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info | |
29 -- LibStub is hereby placed in the Public Domain -- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke | |
30 local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! | |
31 local LibStub = _G[LIBSTUB_MAJOR] | |
32 | |
33 if not LibStub or LibStub.minor < LIBSTUB_MINOR then | |
34 LibStub = LibStub or {libs = {}, minors = {} } | |
35 _G[LIBSTUB_MAJOR] = LibStub | |
36 LibStub.minor = LIBSTUB_MINOR | |
37 | |
38 function LibStub:NewLibrary(major, minor) | |
39 assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") | |
40 minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") | |
41 local oldminor = self.minors[major] | |
42 if oldminor and oldminor >= minor then return nil end | |
43 self.minors[major], self.libs[major] = minor, self.libs[major] or {} | |
44 return self.libs[major], oldminor | |
45 end | |
46 | |
47 function LibStub:GetLibrary(major, silent) | |
48 if not self.libs[major] and not silent then | |
49 error(("Cannot find a library instance of %q."):format(tostring(major)), 2) | |
50 end | |
51 return self.libs[major], self.minors[major] | |
52 end | |
53 | |
54 function LibStub:IterateLibraries() return pairs(self.libs) end | |
55 setmetatable(LibStub, { __call = LibStub.GetLibrary }) | |
56 end | |
57 end | |
58 local LibStub = _G.LibStub | |
59 | |
60 -- If you don't want AceLibrary to enable libraries that are LoadOnDemand but | |
61 -- disabled in the addon screen, set this to true. | |
62 local DONT_ENABLE_LIBRARIES = nil | |
63 | |
64 local function safecall(func,...) | |
65 local success, err = pcall(func,...) | |
66 if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end | |
67 end | |
68 | |
69 local WoW22 = false | |
70 if type(GetBuildInfo) == "function" then | |
71 local success, buildinfo = pcall(GetBuildInfo) | |
72 if success and type(buildinfo) == "string" then | |
73 local num = tonumber(buildinfo:match("^(%d+%.%d+)")) | |
74 if num and num >= 2.2 then | |
75 WoW22 = true | |
76 end | |
77 end | |
78 end | |
79 | |
80 -- @table AceLibrary | |
81 -- @brief System to handle all versioning of libraries. | |
82 local AceLibrary = {} | |
83 local AceLibrary_mt = {} | |
84 setmetatable(AceLibrary, AceLibrary_mt) | |
85 | |
86 local function error(self, message, ...) | |
87 if type(self) ~= "table" then | |
88 return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2) | |
89 end | |
90 | |
91 local stack = debugstack() | |
92 if not message then | |
93 local second = stack:match("\n(.-)\n") | |
94 message = "error raised! " .. second | |
95 else | |
96 local arg = { ... } -- not worried about table creation, as errors don't happen often | |
97 | |
98 for i = 1, #arg do | |
99 arg[i] = tostring(arg[i]) | |
100 end | |
101 for i = 1, 10 do | |
102 table.insert(arg, "nil") | |
103 end | |
104 message = message:format(unpack(arg)) | |
105 end | |
106 | |
107 if getmetatable(self) and getmetatable(self).__tostring then | |
108 message = ("%s: %s"):format(tostring(self), message) | |
109 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then | |
110 message = ("%s: %s"):format(self:GetLibraryVersion(), message) | |
111 elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then | |
112 message = ("%s: %s"):format(self.class:GetLibraryVersion(), message) | |
113 end | |
114 | |
115 local first = stack:gsub("\n.*", "") | |
116 local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1") | |
117 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") | |
118 | |
119 | |
120 local i = 0 | |
121 for s in stack:gmatch("\n([^\n]*)") do | |
122 i = i + 1 | |
123 if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then | |
124 file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1") | |
125 file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") | |
126 break | |
127 end | |
128 end | |
129 local j = 0 | |
130 for s in stack:gmatch("\n([^\n]*)") do | |
131 j = j + 1 | |
132 if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then | |
133 return _G.error(message, j+1) | |
134 end | |
135 end | |
136 return _G.error(message, 2) | |
137 end | |
138 | |
139 local assert | |
140 if not WoW22 then | |
141 function assert(self, condition, message, ...) | |
142 if not condition then | |
143 if not message then | |
144 local stack = debugstack() | |
145 local second = stack:match("\n(.-)\n") | |
146 message = "assertion failed! " .. second | |
147 end | |
148 return error(self, message, ...) | |
149 end | |
150 return condition | |
151 end | |
152 end | |
153 | |
154 local type = type | |
155 local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) | |
156 if type(num) ~= "number" then | |
157 return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) | |
158 elseif type(kind) ~= "string" then | |
159 return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) | |
160 end | |
161 arg = type(arg) | |
162 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then | |
163 local stack = debugstack() | |
164 local func = stack:match("`argCheck'.-([`<].-['>])") | |
165 if not func then | |
166 func = stack:match("([`<].-['>])") | |
167 end | |
168 if kind5 then | |
169 return error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg) | |
170 elseif kind4 then | |
171 return error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg) | |
172 elseif kind3 then | |
173 return error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg) | |
174 elseif kind2 then | |
175 return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) | |
176 else | |
177 return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) | |
178 end | |
179 end | |
180 end | |
181 | |
182 local pcall | |
183 do | |
184 local function check(self, ret, ...) | |
185 if not ret then | |
186 local s = ... | |
187 return error(self, (s:gsub(".-%.lua:%d-: ", ""))) | |
188 else | |
189 return ... | |
190 end | |
191 end | |
192 | |
193 function pcall(self, func, ...) | |
194 return check(self, _G.pcall(func, ...)) | |
195 end | |
196 end | |
197 | |
198 local recurse = {} | |
199 local function addToPositions(t, major) | |
200 if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then | |
201 rawset(t, recurse, true) | |
202 AceLibrary.positions[t] = major | |
203 for k,v in pairs(t) do | |
204 if type(v) == "table" and not rawget(v, recurse) then | |
205 addToPositions(v, major) | |
206 end | |
207 if type(k) == "table" and not rawget(k, recurse) then | |
208 addToPositions(k, major) | |
209 end | |
210 end | |
211 local mt = getmetatable(t) | |
212 if mt and not rawget(mt, recurse) then | |
213 addToPositions(mt, major) | |
214 end | |
215 rawset(t, recurse, nil) | |
216 end | |
217 end | |
218 | |
219 local function svnRevisionToNumber(text) | |
220 local kind = type(text) | |
221 if kind == "number" or tonumber(text) then | |
222 return tonumber(text) | |
223 elseif kind == "string" then | |
224 if text:find("^%$Revision: (%d+) %$$") then | |
225 return tonumber((text:match("^%$Revision: (%d+) %$$"))) | |
226 elseif text:find("^%$Rev: (%d+) %$$") then | |
227 return tonumber((text:match("^%$Rev: (%d+) %$$"))) | |
228 elseif text:find("^%$LastChangedRevision: (%d+) %$$") then | |
229 return tonumber((text:match("^%$LastChangedRevision: (%d+) %$$"))) | |
230 end | |
231 end | |
232 return nil | |
233 end | |
234 | |
235 local crawlReplace | |
236 do | |
237 local recurse = {} | |
238 local function func(t, to, from) | |
239 if recurse[t] then | |
240 return | |
241 end | |
242 recurse[t] = true | |
243 local mt = getmetatable(t) | |
244 setmetatable(t, nil) | |
245 rawset(t, to, rawget(t, from)) | |
246 rawset(t, from, nil) | |
247 for k,v in pairs(t) do | |
248 if v == from then | |
249 t[k] = to | |
250 elseif type(v) == "table" then | |
251 if not recurse[v] then | |
252 func(v, to, from) | |
253 end | |
254 end | |
255 | |
256 if type(k) == "table" then | |
257 if not recurse[k] then | |
258 func(k, to, from) | |
259 end | |
260 end | |
261 end | |
262 setmetatable(t, mt) | |
263 if mt then | |
264 if mt == from then | |
265 setmetatable(t, to) | |
266 elseif not recurse[mt] then | |
267 func(mt, to, from) | |
268 end | |
269 end | |
270 end | |
271 function crawlReplace(t, to, from) | |
272 func(t, to, from) | |
273 for k in pairs(recurse) do | |
274 recurse[k] = nil | |
275 end | |
276 end | |
277 end | |
278 | |
279 -- @function destroyTable | |
280 -- @brief remove all the contents of a table | |
281 -- @param t table to destroy | |
282 local function destroyTable(t) | |
283 setmetatable(t, nil) | |
284 for k,v in pairs(t) do | |
285 t[k] = nil | |
286 end | |
287 end | |
288 | |
289 local function isFrame(frame) | |
290 return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function" | |
291 end | |
292 | |
293 -- @function copyTable | |
294 -- @brief Create a shallow copy of a table and return it. | |
295 -- @param from The table to copy from | |
296 -- @return A shallow copy of the table | |
297 local function copyTable(from, to) | |
298 if not to then | |
299 to = {} | |
300 end | |
301 for k,v in pairs(from) do | |
302 to[k] = v | |
303 end | |
304 setmetatable(to, getmetatable(from)) | |
305 return to | |
306 end | |
307 | |
308 -- @function deepTransfer | |
309 -- @brief Fully transfer all data, keeping proper previous table | |
310 -- backreferences stable. | |
311 -- @param to The table with which data is to be injected into | |
312 -- @param from The table whose data will be injected into the first | |
313 -- @param saveFields If available, a shallow copy of the basic data is saved | |
314 -- in here. | |
315 -- @param list The account of table references | |
316 -- @param list2 The current status on which tables have been traversed. | |
317 local deepTransfer | |
318 do | |
319 -- @function examine | |
320 -- @brief Take account of all the table references to be shared | |
321 -- between the to and from tables. | |
322 -- @param to The table with which data is to be injected into | |
323 -- @param from The table whose data will be injected into the first | |
324 -- @param list An account of the table references | |
325 local function examine(to, from, list, major) | |
326 list[from] = to | |
327 for k,v in pairs(from) do | |
328 if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then | |
329 if from[k] == to[k] then | |
330 list[from[k]] = to[k] | |
331 elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then | |
332 list[from[k]] = from[k] | |
333 elseif not list[from[k]] then | |
334 examine(to[k], from[k], list, major) | |
335 end | |
336 end | |
337 end | |
338 return list | |
339 end | |
340 | |
341 function deepTransfer(to, from, saveFields, major, list, list2) | |
342 setmetatable(to, nil) | |
343 if not list then | |
344 list = {} | |
345 list2 = {} | |
346 examine(to, from, list, major) | |
347 end | |
348 list2[to] = to | |
349 for k,v in pairs(to) do | |
350 if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then | |
351 if saveFields then | |
352 saveFields[k] = v | |
353 end | |
354 to[k] = nil | |
355 elseif v ~= _G then | |
356 if saveFields then | |
357 saveFields[k] = copyTable(v) | |
358 end | |
359 end | |
360 end | |
361 for k in pairs(from) do | |
362 if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then | |
363 if not list2[to[k]] then | |
364 deepTransfer(to[k], from[k], nil, major, list, list2) | |
365 end | |
366 to[k] = list[to[k]] or list2[to[k]] | |
367 else | |
368 rawset(to, k, from[k]) | |
369 end | |
370 end | |
371 setmetatable(to, getmetatable(from)) | |
372 local mt = getmetatable(to) | |
373 if mt then | |
374 if list[mt] then | |
375 setmetatable(to, list[mt]) | |
376 elseif mt.__index and list[mt.__index] then | |
377 mt.__index = list[mt.__index] | |
378 end | |
379 end | |
380 destroyTable(from) | |
381 end | |
382 end | |
383 | |
384 local function TryToEnable(addon) | |
385 if DONT_ENABLE_LIBRARIES then return end | |
386 local isondemand = IsAddOnLoadOnDemand(addon) | |
387 if isondemand then | |
388 local _, _, _, enabled = GetAddOnInfo(addon) | |
389 EnableAddOn(addon) | |
390 local _, _, _, _, loadable = GetAddOnInfo(addon) | |
391 if not loadable and not enabled then | |
392 DisableAddOn(addon) | |
393 end | |
394 | |
395 return loadable | |
396 end | |
397 end | |
398 | |
399 -- @method TryToLoadStandalone | |
400 -- @brief Attempt to find and load a standalone version of the requested library | |
401 -- @param major A string representing the major version | |
402 -- @return If library is found and loaded, true is return. If not loadable, false is returned. | |
403 -- If the library has been requested previously, nil is returned. | |
404 local function TryToLoadStandalone(major) | |
405 if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end | |
406 if AceLibrary.scannedlibs[major] then return end | |
407 | |
408 AceLibrary.scannedlibs[major] = true | |
409 | |
410 local name, _, _, enabled, loadable = GetAddOnInfo(major) | |
411 | |
412 loadable = (enabled and loadable) or TryToEnable(name) | |
413 | |
414 local loaded = false | |
415 if loadable then | |
416 loaded = true | |
417 LoadAddOn(name) | |
418 end | |
419 | |
420 local field = "X-AceLibrary-" .. major | |
421 for i = 1, GetNumAddOns() do | |
422 if GetAddOnMetadata(i, field) then | |
423 name, _, _, enabled, loadable = GetAddOnInfo(i) | |
424 | |
425 loadable = (enabled and loadable) or TryToEnable(name) | |
426 if loadable then | |
427 loaded = true | |
428 LoadAddOn(name) | |
429 end | |
430 end | |
431 end | |
432 return loaded | |
433 end | |
434 | |
435 -- @method IsNewVersion | |
436 -- @brief Obtain whether the supplied version would be an upgrade to the | |
437 -- current version. This allows for bypass code in library | |
438 -- declaration. | |
439 -- @param major A string representing the major version | |
440 -- @param minor An integer or an svn revision string representing the minor version | |
441 -- @return whether the supplied version would be newer than what is | |
442 -- currently available. | |
443 function AceLibrary:IsNewVersion(major, minor) | |
444 argCheck(self, major, 2, "string") | |
445 TryToLoadStandalone(major) | |
446 | |
447 if type(minor) == "string" then | |
448 local m = svnRevisionToNumber(minor) | |
449 if m then | |
450 minor = m | |
451 else | |
452 _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) | |
453 end | |
454 end | |
455 argCheck(self, minor, 3, "number") | |
456 local lib, oldMinor = LibStub:GetLibrary(major, true) | |
457 if lib then | |
458 return oldMinor < minor | |
459 end | |
460 local data = self.libs[major] | |
461 if not data then | |
462 return true | |
463 end | |
464 return data.minor < minor | |
465 end | |
466 | |
467 -- @method HasInstance | |
468 -- @brief Returns whether an instance exists. This allows for optional support of a library. | |
469 -- @param major A string representing the major version. | |
470 -- @param minor (optional) An integer or an svn revision string representing the minor version. | |
471 -- @return Whether an instance exists. | |
472 function AceLibrary:HasInstance(major, minor) | |
473 argCheck(self, major, 2, "string") | |
474 if minor ~= false then | |
475 TryToLoadStandalone(major) | |
476 end | |
477 | |
478 local lib, ver = LibStub:GetLibrary(major, true) | |
479 if not lib and self.libs[major] then | |
480 lib, ver = self.libs[major].instance, self.libs[major].minor | |
481 end | |
482 if minor then | |
483 if type(minor) == "string" then | |
484 local m = svnRevisionToNumber(minor) | |
485 if m then | |
486 minor = m | |
487 else | |
488 _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) | |
489 end | |
490 end | |
491 argCheck(self, minor, 3, "number") | |
492 if not lib then | |
493 return false | |
494 end | |
495 return ver == minor | |
496 end | |
497 return not not lib | |
498 end | |
499 | |
500 -- @method GetInstance | |
501 -- @brief Returns the library with the given major/minor version. | |
502 -- @param major A string representing the major version. | |
503 -- @param minor (optional) An integer or an svn revision string representing the minor version. | |
504 -- @return The library with the given major/minor version. | |
505 function AceLibrary:GetInstance(major, minor) | |
506 argCheck(self, major, 2, "string") | |
507 if minor ~= false then | |
508 TryToLoadStandalone(major) | |
509 end | |
510 | |
511 local data, ver = LibStub:GetLibrary(major, true) | |
512 if not data then | |
513 if self.libs[major] then | |
514 data, ver = self.libs[major].instance, self.libs[major].minor | |
515 else | |
516 _G.error(("Cannot find a library instance of %s."):format(major), 2) | |
517 return | |
518 end | |
519 end | |
520 if minor then | |
521 if type(minor) == "string" then | |
522 local m = svnRevisionToNumber(minor) | |
523 if m then | |
524 minor = m | |
525 else | |
526 _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) | |
527 end | |
528 end | |
529 argCheck(self, minor, 2, "number") | |
530 if ver ~= minor then | |
531 _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2) | |
532 end | |
533 end | |
534 return data | |
535 end | |
536 | |
537 -- Syntax sugar. AceLibrary("FooBar-1.0") | |
538 AceLibrary_mt.__call = AceLibrary.GetInstance | |
539 | |
540 local donothing = function() end | |
541 | |
542 local AceEvent | |
543 | |
544 local tmp = {} | |
545 | |
546 -- @method Register | |
547 -- @brief Registers a new version of a given library. | |
548 -- @param newInstance the library to register | |
549 -- @param major the major version of the library | |
550 -- @param minor the minor version of the library | |
551 -- @param activateFunc (optional) A function to be called when the library is | |
552 -- fully activated. Takes the arguments | |
553 -- (newInstance [, oldInstance, oldDeactivateFunc]). If | |
554 -- oldInstance is given, you should probably call | |
555 -- oldDeactivateFunc(oldInstance). | |
556 -- @param deactivateFunc (optional) A function to be called by a newer library's | |
557 -- activateFunc. | |
558 -- @param externalFunc (optional) A function to be called whenever a new | |
559 -- library is registered. | |
560 function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) | |
561 argCheck(self, newInstance, 2, "table") | |
562 argCheck(self, major, 3, "string") | |
563 if major ~= ACELIBRARY_MAJOR then | |
564 for k,v in pairs(_G) do | |
565 if v == newInstance then | |
566 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) | |
567 end | |
568 end | |
569 end | |
570 if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then | |
571 _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2) | |
572 end | |
573 if type(minor) == "string" then | |
574 local m = svnRevisionToNumber(minor) | |
575 if m then | |
576 minor = m | |
577 else | |
578 _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) | |
579 end | |
580 end | |
581 argCheck(self, minor, 4, "number") | |
582 if math.floor(minor) ~= minor or minor < 0 then | |
583 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor) | |
584 end | |
585 argCheck(self, activateFunc, 5, "function", "nil") | |
586 argCheck(self, deactivateFunc, 6, "function", "nil") | |
587 argCheck(self, externalFunc, 7, "function", "nil") | |
588 if not deactivateFunc then | |
589 deactivateFunc = donothing | |
590 end | |
591 local data = self.libs[major] | |
592 if not data then | |
593 -- This is new | |
594 if LibStub:GetLibrary(major, true) then | |
595 error(self, "Cannot register library %q. It is already registered with LibStub.", major) | |
596 end | |
597 local instance = LibStub:NewLibrary(major, minor) | |
598 copyTable(newInstance, instance) | |
599 crawlReplace(instance, instance, newInstance) | |
600 destroyTable(newInstance) | |
601 if AceLibrary == newInstance then | |
602 self = instance | |
603 AceLibrary = instance | |
604 end | |
605 self.libs[major] = { | |
606 instance = instance, | |
607 minor = minor, | |
608 deactivateFunc = deactivateFunc, | |
609 externalFunc = externalFunc, | |
610 } | |
611 rawset(instance, 'GetLibraryVersion', function(self) | |
612 return major, minor | |
613 end) | |
614 if not rawget(instance, 'error') then | |
615 rawset(instance, 'error', error) | |
616 end | |
617 if not WoW22 and not rawget(instance, 'assert') then | |
618 rawset(instance, 'assert', assert) | |
619 end | |
620 if not rawget(instance, 'argCheck') then | |
621 rawset(instance, 'argCheck', argCheck) | |
622 end | |
623 if not rawget(instance, 'pcall') then | |
624 rawset(instance, 'pcall', pcall) | |
625 end | |
626 addToPositions(instance, major) | |
627 if activateFunc then | |
628 safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil | |
629 | |
630 --[[ if major ~= ACELIBRARY_MAJOR then | |
631 for k,v in pairs(_G) do | |
632 if v == instance then | |
633 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) | |
634 end | |
635 end | |
636 end]] | |
637 end | |
638 | |
639 if externalFunc then | |
640 for k, data_instance in LibStub:IterateLibraries() do -- all libraries | |
641 tmp[k] = data_instance | |
642 end | |
643 for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub | |
644 tmp[k] = data.instance | |
645 end | |
646 for k, data_instance in pairs(tmp) do | |
647 if k ~= major then | |
648 safecall(externalFunc, instance, k, data_instance) | |
649 end | |
650 tmp[k] = nil | |
651 end | |
652 end | |
653 | |
654 for k,data in pairs(self.libs) do -- only Ace libraries | |
655 if k ~= major and data.externalFunc then | |
656 safecall(data.externalFunc, data.instance, major, instance) | |
657 end | |
658 end | |
659 if major == "AceEvent-2.0" then | |
660 AceEvent = instance | |
661 end | |
662 if AceEvent then | |
663 AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance) | |
664 end | |
665 | |
666 return instance | |
667 end | |
668 if minor <= data.minor then | |
669 -- This one is already obsolete, raise an error. | |
670 _G.error(("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end"):format(major, data.minor, minor, major, minor), 2) | |
671 return | |
672 end | |
673 local instance = data.instance | |
674 -- This is an update | |
675 local oldInstance = {} | |
676 | |
677 local libStubInstance = LibStub:GetLibrary(major, true) | |
678 if not libStubInstance then -- non-LibStub AceLibrary registered the library | |
679 -- pass | |
680 elseif libStubInstance ~= instance then | |
681 error(self, "Cannot register library %q. It is already registered with LibStub.", major) | |
682 else | |
683 LibStub:NewLibrary(major, minor) -- upgrade the minor version | |
684 end | |
685 | |
686 addToPositions(newInstance, major) | |
687 local isAceLibrary = (AceLibrary == newInstance) | |
688 local old_error, old_assert, old_argCheck, old_pcall | |
689 if isAceLibrary then | |
690 self = instance | |
691 AceLibrary = instance | |
692 | |
693 old_error = instance.error | |
694 if not WoW22 then | |
695 old_assert = instance.assert | |
696 end | |
697 old_argCheck = instance.argCheck | |
698 old_pcall = instance.pcall | |
699 | |
700 self.error = error | |
701 if not WoW22 then | |
702 self.assert = assert | |
703 end | |
704 self.argCheck = argCheck | |
705 self.pcall = pcall | |
706 end | |
707 deepTransfer(instance, newInstance, oldInstance, major) | |
708 crawlReplace(instance, instance, newInstance) | |
709 local oldDeactivateFunc = data.deactivateFunc | |
710 data.minor = minor | |
711 data.deactivateFunc = deactivateFunc | |
712 data.externalFunc = externalFunc | |
713 rawset(instance, 'GetLibraryVersion', function() | |
714 return major, minor | |
715 end) | |
716 if not rawget(instance, 'error') then | |
717 rawset(instance, 'error', error) | |
718 end | |
719 if not WoW22 and not rawget(instance, 'assert') then | |
720 rawset(instance, 'assert', assert) | |
721 end | |
722 if not rawget(instance, 'argCheck') then | |
723 rawset(instance, 'argCheck', argCheck) | |
724 end | |
725 if not rawget(instance, 'pcall') then | |
726 rawset(instance, 'pcall', pcall) | |
727 end | |
728 if isAceLibrary then | |
729 for _,v in pairs(self.libs) do | |
730 local i = type(v) == "table" and v.instance | |
731 if type(i) == "table" then | |
732 if not rawget(i, 'error') or i.error == old_error then | |
733 rawset(i, 'error', error) | |
734 end | |
735 if not WoW22 and (not rawget(i, 'assert') or i.assert == old_assert) then | |
736 rawset(i, 'assert', assert) | |
737 end | |
738 if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then | |
739 rawset(i, 'argCheck', argCheck) | |
740 end | |
741 if not rawget(i, 'pcall') or i.pcall == old_pcall then | |
742 rawset(i, 'pcall', pcall) | |
743 end | |
744 end | |
745 end | |
746 end | |
747 if activateFunc then | |
748 safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) | |
749 | |
750 --[[ if major ~= ACELIBRARY_MAJOR then | |
751 for k,v in pairs(_G) do | |
752 if v == instance then | |
753 geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) | |
754 end | |
755 end | |
756 end]] | |
757 else | |
758 safecall(oldDeactivateFunc, oldInstance) | |
759 end | |
760 oldInstance = nil | |
761 | |
762 if externalFunc then | |
763 for k, data_instance in LibStub:IterateLibraries() do -- all libraries | |
764 tmp[k] = data_instance | |
765 end | |
766 for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub | |
767 tmp[k] = data.instance | |
768 end | |
769 for k, data_instance in pairs(tmp) do | |
770 if k ~= major then | |
771 safecall(externalFunc, instance, k, data_instance) | |
772 end | |
773 tmp[k] = nil | |
774 end | |
775 end | |
776 | |
777 return instance | |
778 end | |
779 | |
780 function AceLibrary:IterateLibraries() | |
781 local t = {} | |
782 for major, instance in LibStub:IterateLibraries() do | |
783 t[major] = instance | |
784 end | |
785 for major, data in pairs(self.libs) do | |
786 t[major] = data.instance | |
787 end | |
788 return pairs(t) | |
789 end | |
790 | |
791 local function manuallyFinalize(major, instance) | |
792 if AceLibrary.libs[major] then | |
793 -- don't work on Ace libraries | |
794 return | |
795 end | |
796 local finalizedExternalLibs = AceLibrary.finalizedExternalLibs | |
797 if finalizedExternalLibs[major] then | |
798 return | |
799 end | |
800 finalizedExternalLibs[major] = true | |
801 | |
802 for k,data in pairs(AceLibrary.libs) do -- only Ace libraries | |
803 if k ~= major and data.externalFunc then | |
804 safecall(data.externalFunc, data.instance, major, instance) | |
805 end | |
806 end | |
807 end | |
808 | |
809 -- @function Activate | |
810 -- @brief The activateFunc for AceLibrary itself. Called when | |
811 -- AceLibrary properly registers. | |
812 -- @param self Reference to AceLibrary | |
813 -- @param oldLib (optional) Reference to an old version of AceLibrary | |
814 -- @param oldDeactivate (optional) Function to deactivate the old lib | |
815 local function activate(self, oldLib, oldDeactivate) | |
816 AceLibrary = self | |
817 if not self.libs then | |
818 self.libs = oldLib and oldLib.libs or {} | |
819 self.scannedlibs = oldLib and oldLib.scannedlibs or {} | |
820 end | |
821 if not self.positions then | |
822 self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" }) | |
823 end | |
824 self.finalizedExternalLibs = oldLib and oldLib.finalizedExternalLibs or {} | |
825 self.frame = oldLib and oldLib.frame or CreateFrame("Frame") | |
826 self.frame:UnregisterAllEvents() | |
827 self.frame:RegisterEvent("ADDON_LOADED") | |
828 self.frame:SetScript("OnEvent", function() | |
829 for major, instance in LibStub:IterateLibraries() do | |
830 manuallyFinalize(major, instance) | |
831 end | |
832 end) | |
833 for major, instance in LibStub:IterateLibraries() do | |
834 manuallyFinalize(major, instance) | |
835 end | |
836 | |
837 -- Expose the library in the global environment | |
838 _G[ACELIBRARY_MAJOR] = self | |
839 | |
840 if oldDeactivate then | |
841 oldDeactivate(oldLib) | |
842 end | |
843 end | |
844 | |
845 if not previous then | |
846 previous = AceLibrary | |
847 end | |
848 if not previous.libs then | |
849 previous.libs = {} | |
850 end | |
851 AceLibrary.libs = previous.libs | |
852 if not previous.positions then | |
853 previous.positions = setmetatable({}, { __mode = "k" }) | |
854 end | |
855 AceLibrary.positions = previous.positions | |
856 AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate, nil) |