comparison lib/AceLibrary/AceLibrary.lua @ 33:c54c481ad0ed

- Moved bar control frame from ConfigUI to Bar - Added LICENSE.txt - added profile management options - other minor cleanup
author Flick <flickerstreak@gmail.com>
date Thu, 03 Apr 2008 20:25:40 +0000
parents
children
comparison
equal deleted inserted replaced
32:821b2b7edff1 33:c54c481ad0ed
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)