annotate libs/AceLibrary/AceLibrary.lua @ 1:c11ca1d8ed91

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