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