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