diff lib/AceLibrary/AceLibrary.lua @ 22:1b9323256a1b

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