annotate lib/AceLibrary/AceLibrary.lua @ 71:3d2cef5dc459

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