annotate libs/AceLibrary/AceLibrary.lua @ 7:f920db5fc6b1

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