Mercurial > wow > reaction
diff modules/ReAction_ConfigUI/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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/ReAction_ConfigUI/lib/AceLibrary/AceLibrary.lua Mon Mar 17 18:24:53 2008 +0000 @@ -0,0 +1,856 @@ +--[[ +Name: AceLibrary +Revision: $Rev: 49421 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Iriel (iriel@vigilance-committee.org) + Tekkub (tekkub@gmail.com) + Revision: $Rev: 49421 $ +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLibrary +SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary +Description: Versioning library to handle other library instances, upgrading, + and proper access. + It also provides a base for libraries to work off of, providing + proper error tools. It is handy because all the errors occur in the + file that called it, not in the library file itself. +Dependencies: None +License: LGPL v2.1 +]] + +local ACELIBRARY_MAJOR = "AceLibrary" +local ACELIBRARY_MINOR = "$Revision: 49421 $" + +local _G = getfenv(0) +local previous = _G[ACELIBRARY_MAJOR] +if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end + +do + -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info + -- LibStub is hereby placed in the Public Domain -- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke + local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! + local LibStub = _G[LIBSTUB_MAJOR] + + if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) + end +end +local LibStub = _G.LibStub + +-- If you don't want AceLibrary to enable libraries that are LoadOnDemand but +-- disabled in the addon screen, set this to true. +local DONT_ENABLE_LIBRARIES = nil + +local function safecall(func,...) + local success, err = pcall(func,...) + if not success then geterrorhandler()(err:find("%.lua:%d+:") and err or (debugstack():match("\n(.-: )in.-\n") or "") .. err) end +end + +local WoW22 = false +if type(GetBuildInfo) == "function" then + local success, buildinfo = pcall(GetBuildInfo) + if success and type(buildinfo) == "string" then + local num = tonumber(buildinfo:match("^(%d+%.%d+)")) + if num and num >= 2.2 then + WoW22 = true + end + end +end + +-- @table AceLibrary +-- @brief System to handle all versioning of libraries. +local AceLibrary = {} +local AceLibrary_mt = {} +setmetatable(AceLibrary, AceLibrary_mt) + +local function error(self, message, ...) + if type(self) ~= "table" then + return _G.error(("Bad argument #1 to `error' (table expected, got %s)"):format(type(self)), 2) + end + + local stack = debugstack() + if not message then + local second = stack:match("\n(.-)\n") + message = "error raised! " .. second + else + local arg = { ... } -- not worried about table creation, as errors don't happen often + + for i = 1, #arg do + arg[i] = tostring(arg[i]) + end + for i = 1, 10 do + table.insert(arg, "nil") + end + message = message:format(unpack(arg)) + end + + if getmetatable(self) and getmetatable(self).__tostring then + message = ("%s: %s"):format(tostring(self), message) + elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then + message = ("%s: %s"):format(self:GetLibraryVersion(), message) + elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then + message = ("%s: %s"):format(self.class:GetLibraryVersion(), message) + end + + local first = stack:gsub("\n.*", "") + local file = first:gsub(".*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + + + local i = 0 + for s in stack:gmatch("\n([^\n]*)") do + i = i + 1 + if not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then + file = s:gsub("^.*\\(.*).lua:%d+: .*", "%1") + file = file:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + break + end + end + local j = 0 + for s in stack:gmatch("\n([^\n]*)") do + j = j + 1 + if j > i and not s:find(file .. "%.lua:%d+:") and not s:find("%(tail call%)") then + return _G.error(message, j+1) + end + end + return _G.error(message, 2) +end + +local assert +if not WoW22 then + function assert(self, condition, message, ...) + if not condition then + if not message then + local stack = debugstack() + local second = stack:match("\n(.-)\n") + message = "assertion failed! " .. second + end + return error(self, message, ...) + end + return condition + end +end + +local type = type +local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) + if type(num) ~= "number" then + return error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) + elseif type(kind) ~= "string" then + return error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) + end + arg = type(arg) + if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then + local stack = debugstack() + local func = stack:match("`argCheck'.-([`<].-['>])") + if not func then + func = stack:match("([`<].-['>])") + end + if kind5 then + 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) + elseif kind4 then + 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) + elseif kind3 then + 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) + elseif kind2 then + return error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) + else + return error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) + end + end +end + +local pcall +do + local function check(self, ret, ...) + if not ret then + local s = ... + return error(self, (s:gsub(".-%.lua:%d-: ", ""))) + else + return ... + end + end + + function pcall(self, func, ...) + return check(self, _G.pcall(func, ...)) + end +end + +local recurse = {} +local function addToPositions(t, major) + if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then + rawset(t, recurse, true) + AceLibrary.positions[t] = major + for k,v in pairs(t) do + if type(v) == "table" and not rawget(v, recurse) then + addToPositions(v, major) + end + if type(k) == "table" and not rawget(k, recurse) then + addToPositions(k, major) + end + end + local mt = getmetatable(t) + if mt and not rawget(mt, recurse) then + addToPositions(mt, major) + end + rawset(t, recurse, nil) + end +end + +local function svnRevisionToNumber(text) + local kind = type(text) + if kind == "number" or tonumber(text) then + return tonumber(text) + elseif kind == "string" then + if text:find("^%$Revision: (%d+) %$$") then + return tonumber((text:match("^%$Revision: (%d+) %$$"))) + elseif text:find("^%$Rev: (%d+) %$$") then + return tonumber((text:match("^%$Rev: (%d+) %$$"))) + elseif text:find("^%$LastChangedRevision: (%d+) %$$") then + return tonumber((text:match("^%$LastChangedRevision: (%d+) %$$"))) + end + end + return nil +end + +local crawlReplace +do + local recurse = {} + local function func(t, to, from) + if recurse[t] then + return + end + recurse[t] = true + local mt = getmetatable(t) + setmetatable(t, nil) + rawset(t, to, rawget(t, from)) + rawset(t, from, nil) + for k,v in pairs(t) do + if v == from then + t[k] = to + elseif type(v) == "table" then + if not recurse[v] then + func(v, to, from) + end + end + + if type(k) == "table" then + if not recurse[k] then + func(k, to, from) + end + end + end + setmetatable(t, mt) + if mt then + if mt == from then + setmetatable(t, to) + elseif not recurse[mt] then + func(mt, to, from) + end + end + end + function crawlReplace(t, to, from) + func(t, to, from) + for k in pairs(recurse) do + recurse[k] = nil + end + end +end + +-- @function destroyTable +-- @brief remove all the contents of a table +-- @param t table to destroy +local function destroyTable(t) + setmetatable(t, nil) + for k,v in pairs(t) do + t[k] = nil + end +end + +local function isFrame(frame) + 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" +end + +-- @function copyTable +-- @brief Create a shallow copy of a table and return it. +-- @param from The table to copy from +-- @return A shallow copy of the table +local function copyTable(from, to) + if not to then + to = {} + end + for k,v in pairs(from) do + to[k] = v + end + setmetatable(to, getmetatable(from)) + return to +end + +-- @function deepTransfer +-- @brief Fully transfer all data, keeping proper previous table +-- backreferences stable. +-- @param to The table with which data is to be injected into +-- @param from The table whose data will be injected into the first +-- @param saveFields If available, a shallow copy of the basic data is saved +-- in here. +-- @param list The account of table references +-- @param list2 The current status on which tables have been traversed. +local deepTransfer +do + -- @function examine + -- @brief Take account of all the table references to be shared + -- between the to and from tables. + -- @param to The table with which data is to be injected into + -- @param from The table whose data will be injected into the first + -- @param list An account of the table references + local function examine(to, from, list, major) + list[from] = to + for k,v in pairs(from) do + if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then + if from[k] == to[k] then + list[from[k]] = to[k] + elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then + list[from[k]] = from[k] + elseif not list[from[k]] then + examine(to[k], from[k], list, major) + end + end + end + return list + end + + function deepTransfer(to, from, saveFields, major, list, list2) + setmetatable(to, nil) + if not list then + list = {} + list2 = {} + examine(to, from, list, major) + end + list2[to] = to + for k,v in pairs(to) do + if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then + if saveFields then + saveFields[k] = v + end + to[k] = nil + elseif v ~= _G then + if saveFields then + saveFields[k] = copyTable(v) + end + end + end + for k in pairs(from) do + if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then + if not list2[to[k]] then + deepTransfer(to[k], from[k], nil, major, list, list2) + end + to[k] = list[to[k]] or list2[to[k]] + else + rawset(to, k, from[k]) + end + end + setmetatable(to, getmetatable(from)) + local mt = getmetatable(to) + if mt then + if list[mt] then + setmetatable(to, list[mt]) + elseif mt.__index and list[mt.__index] then + mt.__index = list[mt.__index] + end + end + destroyTable(from) + end +end + +local function TryToEnable(addon) + if DONT_ENABLE_LIBRARIES then return end + local isondemand = IsAddOnLoadOnDemand(addon) + if isondemand then + local _, _, _, enabled = GetAddOnInfo(addon) + EnableAddOn(addon) + local _, _, _, _, loadable = GetAddOnInfo(addon) + if not loadable and not enabled then + DisableAddOn(addon) + end + + return loadable + end +end + +-- @method TryToLoadStandalone +-- @brief Attempt to find and load a standalone version of the requested library +-- @param major A string representing the major version +-- @return If library is found and loaded, true is return. If not loadable, false is returned. +-- If the library has been requested previously, nil is returned. +local function TryToLoadStandalone(major) + if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end + if AceLibrary.scannedlibs[major] then return end + + AceLibrary.scannedlibs[major] = true + + local name, _, _, enabled, loadable = GetAddOnInfo(major) + + loadable = (enabled and loadable) or TryToEnable(name) + + local loaded = false + if loadable then + loaded = true + LoadAddOn(name) + end + + local field = "X-AceLibrary-" .. major + for i = 1, GetNumAddOns() do + if GetAddOnMetadata(i, field) then + name, _, _, enabled, loadable = GetAddOnInfo(i) + + loadable = (enabled and loadable) or TryToEnable(name) + if loadable then + loaded = true + LoadAddOn(name) + end + end + end + return loaded +end + +-- @method IsNewVersion +-- @brief Obtain whether the supplied version would be an upgrade to the +-- current version. This allows for bypass code in library +-- declaration. +-- @param major A string representing the major version +-- @param minor An integer or an svn revision string representing the minor version +-- @return whether the supplied version would be newer than what is +-- currently available. +function AceLibrary:IsNewVersion(major, minor) + argCheck(self, major, 2, "string") + TryToLoadStandalone(major) + + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 3, "number") + local lib, oldMinor = LibStub:GetLibrary(major, true) + if lib then + return oldMinor < minor + end + local data = self.libs[major] + if not data then + return true + end + return data.minor < minor +end + +-- @method HasInstance +-- @brief Returns whether an instance exists. This allows for optional support of a library. +-- @param major A string representing the major version. +-- @param minor (optional) An integer or an svn revision string representing the minor version. +-- @return Whether an instance exists. +function AceLibrary:HasInstance(major, minor) + argCheck(self, major, 2, "string") + if minor ~= false then + TryToLoadStandalone(major) + end + + local lib, ver = LibStub:GetLibrary(major, true) + if not lib and self.libs[major] then + lib, ver = self.libs[major].instance, self.libs[major].minor + end + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 3, "number") + if not lib then + return false + end + return ver == minor + end + return not not lib +end + +-- @method GetInstance +-- @brief Returns the library with the given major/minor version. +-- @param major A string representing the major version. +-- @param minor (optional) An integer or an svn revision string representing the minor version. +-- @return The library with the given major/minor version. +function AceLibrary:GetInstance(major, minor) + argCheck(self, major, 2, "string") + if minor ~= false then + TryToLoadStandalone(major) + end + + local data, ver = LibStub:GetLibrary(major, true) + if not data then + if self.libs[major] then + data, ver = self.libs[major].instance, self.libs[major].minor + else + _G.error(("Cannot find a library instance of %s."):format(major), 2) + return + end + end + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 2, "number") + if ver ~= minor then + _G.error(("Cannot find a library instance of %s, minor version %d."):format(major, minor), 2) + end + end + return data +end + +-- Syntax sugar. AceLibrary("FooBar-1.0") +AceLibrary_mt.__call = AceLibrary.GetInstance + +local donothing = function() end + +local AceEvent + +local tmp = {} + +-- @method Register +-- @brief Registers a new version of a given library. +-- @param newInstance the library to register +-- @param major the major version of the library +-- @param minor the minor version of the library +-- @param activateFunc (optional) A function to be called when the library is +-- fully activated. Takes the arguments +-- (newInstance [, oldInstance, oldDeactivateFunc]). If +-- oldInstance is given, you should probably call +-- oldDeactivateFunc(oldInstance). +-- @param deactivateFunc (optional) A function to be called by a newer library's +-- activateFunc. +-- @param externalFunc (optional) A function to be called whenever a new +-- library is registered. +function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) + argCheck(self, newInstance, 2, "table") + argCheck(self, major, 3, "string") + if major ~= ACELIBRARY_MAJOR then + for k,v in pairs(_G) do + if v == newInstance then + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) + end + end + end + if major ~= ACELIBRARY_MAJOR and not major:find("^[%a%-][%a%d%-]*%-%d+%.%d+$") then + _G.error(string.format("Bad argument #3 to `Register'. Must be in the form of \"Name-1.0\". %q is not appropriate", major), 2) + end + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate"):format(minor), 2) + end + end + argCheck(self, minor, 4, "number") + if math.floor(minor) ~= minor or minor < 0 then + error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor) + end + argCheck(self, activateFunc, 5, "function", "nil") + argCheck(self, deactivateFunc, 6, "function", "nil") + argCheck(self, externalFunc, 7, "function", "nil") + if not deactivateFunc then + deactivateFunc = donothing + end + local data = self.libs[major] + if not data then + -- This is new + if LibStub:GetLibrary(major, true) then + error(self, "Cannot register library %q. It is already registered with LibStub.", major) + end + local instance = LibStub:NewLibrary(major, minor) + copyTable(newInstance, instance) + crawlReplace(instance, instance, newInstance) + destroyTable(newInstance) + if AceLibrary == newInstance then + self = instance + AceLibrary = instance + end + self.libs[major] = { + instance = instance, + minor = minor, + deactivateFunc = deactivateFunc, + externalFunc = externalFunc, + } + rawset(instance, 'GetLibraryVersion', function(self) + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + end + if not WoW22 and not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + addToPositions(instance, major) + if activateFunc then + safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil + +--[[ if major ~= ACELIBRARY_MAJOR then + for k,v in pairs(_G) do + if v == instance then + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) + end + end + end]] + end + + if externalFunc then + for k, data_instance in LibStub:IterateLibraries() do -- all libraries + tmp[k] = data_instance + end + for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub + tmp[k] = data.instance + end + for k, data_instance in pairs(tmp) do + if k ~= major then + safecall(externalFunc, instance, k, data_instance) + end + tmp[k] = nil + end + end + + for k,data in pairs(self.libs) do -- only Ace libraries + if k ~= major and data.externalFunc then + safecall(data.externalFunc, data.instance, major, instance) + end + end + if major == "AceEvent-2.0" then + AceEvent = instance + end + if AceEvent then + AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance) + end + + return instance + end + if minor <= data.minor then + -- This one is already obsolete, raise an error. + _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) + return + end + local instance = data.instance + -- This is an update + local oldInstance = {} + + local libStubInstance = LibStub:GetLibrary(major, true) + if not libStubInstance then -- non-LibStub AceLibrary registered the library + -- pass + elseif libStubInstance ~= instance then + error(self, "Cannot register library %q. It is already registered with LibStub.", major) + else + LibStub:NewLibrary(major, minor) -- upgrade the minor version + end + + addToPositions(newInstance, major) + local isAceLibrary = (AceLibrary == newInstance) + local old_error, old_assert, old_argCheck, old_pcall + if isAceLibrary then + self = instance + AceLibrary = instance + + old_error = instance.error + if not WoW22 then + old_assert = instance.assert + end + old_argCheck = instance.argCheck + old_pcall = instance.pcall + + self.error = error + if not WoW22 then + self.assert = assert + end + self.argCheck = argCheck + self.pcall = pcall + end + deepTransfer(instance, newInstance, oldInstance, major) + crawlReplace(instance, instance, newInstance) + local oldDeactivateFunc = data.deactivateFunc + data.minor = minor + data.deactivateFunc = deactivateFunc + data.externalFunc = externalFunc + rawset(instance, 'GetLibraryVersion', function() + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + end + if not WoW22 and not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + if isAceLibrary then + for _,v in pairs(self.libs) do + local i = type(v) == "table" and v.instance + if type(i) == "table" then + if not rawget(i, 'error') or i.error == old_error then + rawset(i, 'error', error) + end + if not WoW22 and (not rawget(i, 'assert') or i.assert == old_assert) then + rawset(i, 'assert', assert) + end + if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then + rawset(i, 'argCheck', argCheck) + end + if not rawget(i, 'pcall') or i.pcall == old_pcall then + rawset(i, 'pcall', pcall) + end + end + end + end + if activateFunc then + safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) + +--[[ if major ~= ACELIBRARY_MAJOR then + for k,v in pairs(_G) do + if v == instance then + geterrorhandler()((debugstack():match("(.-: )in.-\n") or "") .. ("Cannot register library %q. It is part of the global table in _G[%q]."):format(major, k)) + end + end + end]] + else + safecall(oldDeactivateFunc, oldInstance) + end + oldInstance = nil + + if externalFunc then + for k, data_instance in LibStub:IterateLibraries() do -- all libraries + tmp[k] = data_instance + end + for k, data in pairs(self.libs) do -- Ace libraries which may not have been registered with LibStub + tmp[k] = data.instance + end + for k, data_instance in pairs(tmp) do + if k ~= major then + safecall(externalFunc, instance, k, data_instance) + end + tmp[k] = nil + end + end + + return instance +end + +function AceLibrary:IterateLibraries() + local t = {} + for major, instance in LibStub:IterateLibraries() do + t[major] = instance + end + for major, data in pairs(self.libs) do + t[major] = data.instance + end + return pairs(t) +end + +local function manuallyFinalize(major, instance) + if AceLibrary.libs[major] then + -- don't work on Ace libraries + return + end + local finalizedExternalLibs = AceLibrary.finalizedExternalLibs + if finalizedExternalLibs[major] then + return + end + finalizedExternalLibs[major] = true + + for k,data in pairs(AceLibrary.libs) do -- only Ace libraries + if k ~= major and data.externalFunc then + safecall(data.externalFunc, data.instance, major, instance) + end + end +end + +-- @function Activate +-- @brief The activateFunc for AceLibrary itself. Called when +-- AceLibrary properly registers. +-- @param self Reference to AceLibrary +-- @param oldLib (optional) Reference to an old version of AceLibrary +-- @param oldDeactivate (optional) Function to deactivate the old lib +local function activate(self, oldLib, oldDeactivate) + AceLibrary = self + if not self.libs then + self.libs = oldLib and oldLib.libs or {} + self.scannedlibs = oldLib and oldLib.scannedlibs or {} + end + if not self.positions then + self.positions = oldLib and oldLib.positions or setmetatable({}, { __mode = "k" }) + end + self.finalizedExternalLibs = oldLib and oldLib.finalizedExternalLibs or {} + self.frame = oldLib and oldLib.frame or CreateFrame("Frame") + self.frame:UnregisterAllEvents() + self.frame:RegisterEvent("ADDON_LOADED") + self.frame:SetScript("OnEvent", function() + for major, instance in LibStub:IterateLibraries() do + manuallyFinalize(major, instance) + end + end) + for major, instance in LibStub:IterateLibraries() do + manuallyFinalize(major, instance) + end + + -- Expose the library in the global environment + _G[ACELIBRARY_MAJOR] = self + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +if not previous then + previous = AceLibrary +end +if not previous.libs then + previous.libs = {} +end +AceLibrary.libs = previous.libs +if not previous.positions then + previous.positions = setmetatable({}, { __mode = "k" }) +end +AceLibrary.positions = previous.positions +AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate, nil)