view DependencyLoader/Tree.lua @ 12:b230b94d4487

fixed Addon.lua to use the unhooked EnableAddOn (still needs to be changed to grab from the interface) improved the error message when creating an Addon object on a Blizzard addon (will add direct support later) implemented the hooks on EnableAddOn and LoadAddOn rearranged functions inside Tree.lua, with some edits copied OptDeps from main module to the bootstrap module, to delegate loading to the client when possible
author mckenziemc
date Fri, 10 Dec 2010 00:21:17 -0800
parents e0a4a8b5b389
children a46bf694050c
line wrap: on
line source
--	Tree
--	Represents a recursive "tree" of addon 
--	dependencies rooted at a specific addon.


local addonName, addonTable = ...


--	FIXME: prevent infinite loops in the recursive functions


local Tree, tree = addonTable:NewClass("Tree")


Tree.trees = {}


--	internal
--	Creates a new tree object
--	@param	root	Name, index, or Addon object of the root addon.
function Tree:New(root)
	if type(root) ~= "table" then
		root = addonTable.classes.Addon:Get(root)
	end
	
	local instance = {}
	setmetatable(instance, self.instanceMetatable)
	
	instance.root = root
	
	return instance
end


---	Retrieves the tree rooted at the specified addon
--	@param	root	Name, index, or Addon object of the root.
function Tree:Get(root)
	if type(root) ~= "table" then
		root = addonTable.classes.Addon:Get(root)
	end
	
	local tree = self.trees[root]
	
	if tree then
		return tree
	else
		tree = self:New(root)
		self.trees[root] = tree
		return tree
	end
end


--	Checks if the tree rooted at the specified addon 
--	can be loaded if all dependencies are enabled 
--	and the UI is reloaded.
--	Basically makes sure all dependencies are installed.
function tree:CanLoad()
	if not self.root:CanLoad() then
		return false
	end

	--	check all the dependencies
	local dependencies = {self.root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local dep = addonTable.classes.Addon:Get(depName)
		
		if not dep:CanLoad() then
			return false
		end
		
		local depTree = Tree:Get(depName)
		
		if not depTree:CanLoad() then
			return false		--	missing dependency
		end
	end
	
	return true
end


---	Checks if the tree can be loaded on demand.
--	Does not consider force-loading; dependencies must 
--	be enabled and LoD, or loaded
function tree:CanLoD()
	--	since this will be used recursively, return true if 
	--	this is already loaded.
	if self.root:IsLoaded() then
		return true
	end
	
	--	true if all dependencies are loaded or LoD
	
	if not self.root:CanLoD() then
		return false
	end
	
	--	now check dependencies recursively
	local dependencies = {self.root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local depTree = Tree:Get(depName)
		
		if not depTree:CanLoD() then
			return false
		end
	end
	
	return true
end


---	Checks if this tree can be loaded on demand as long as
--	force-loading is allowed for preparing dependencies.
function tree:CanLoDWithForce()
	if not self.root:CanLoD() then
		return false
	end
	
	--	now check dependencies recursively
	local dependencies = {self.root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local dep = addonTable.classes.Addon:Get(depName)
		local depTree = Tree:Get(depName)
		
		if not dep:CanLoad() then
			return false
		end
		
		if dep:CanLoD() then
			if not depTree:CanLoDWithForce() then
				return false
			end
		elseif dep:CanForceLoad() then
			if not depTree:CanForceLoad() then
				return
			end
		end
	end
	
	return true
end



---	Checks if this tree can be force-loaded.
--	Does not check user settings nor if force-loading is actually available.
--	@return	canForceLoad	True if this tree can be force loaded, false otherwise
function tree:CanForceLoad()
	--	TODO: remove redundencies (for now they're here for design flexibility)
	
	--	this addon must be loaded, able to load-on-demand, or able to force-load
	if self.root:IsLoaded() then
		return true
	end
	
	if not self.root:CanLoad() then
		return false
	end
	
	if not self.root:CanForceLoad() then
		return false
	end
	
	--	now check dependencies recursively
	local dependencies = {self.root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local dep = addonTable.classes.Addon:Get(depName)
		local depTree = Tree:Get(depName)
		
		if not dep:CanLoad() then
			return false
		end
		
		if not dep:CanLoD() and not dep:CanForceLoad() then
			return false
		end
		
		if not depTree:CanForceLoad() then
			return false
		end
	end
	
	return
end



--[[
--	Loads the tree rooted at the specified addon.
--	FIXME: load the root too or not?
--	Supports both LoD addons and those that require force-loading.
function tree:Load(addon)
	--	don't check if the tree can actually be loaded.
	--	external code should do that itself to check if it 
	--	should even call this at all.

	if self:ForceLoadAvailable() then
		--	LoD trees can also be force-loaded
		self:ForceLoadTree(addon)
	else
		self:LoadLoDTree(addon)
	end
end
]]


function tree:PrepareForReload()
		--	if force-loading is enabled then we have to queue this if the addon isn't lod
	local function callback()
		self.root:Enable()
		
		--	check dependencies
		local dependencies = {self.root:GetDependencies()}
		for i, depName in pairs(dependencies) do
			Tree:Get(depName):PrepareForReload()
		end

		--	prepare embeds, if they are available separately
		local embeds = {self.root:GetEmbeds(addon)}
		for i, embedName in pairs(embeds) do
			Tree:Get(embedName):PrepareForReload()
		end
	end
	
	if IsLoggedIn() then
		callback()
	else
		--	TODO: replace with cleaner alternative?
		local frame = CreateFrame("Frame")
		frame:SetScript("OnEvent", callback)
		frame:RegisterEvent("PLAYER_LOGIN")
	end
end


--	I think the problem this solves is a major issue with 
--	migrating to separate libs. think about it more and document 
--	here and in project description
function tree:PrepareForLoD()
	--	assume root is LoD
	
	--	check dependencies
	local dependencies = {self.root:GetDependencies(addon)}
	for i, depName in pairs(dependencies) do
		local depTree = Tree:Get(depName)
		depTree:PrepareForLoD()
		
		--[[
		if dep:CanLoD() then
			--	don't load it now but make sure its dependencies are prepared
			self:PrepareLoDTree(depName)
		else
			--	FIXME: if it's already loaded
			--	force-load it now so we can load the parent on demand
			self:ForceLoadTree(depName)
		end
		]]
	end

	--	prepare embeds, if they are available separately
	local embeds = {self.root:GetEmbeds(addon)}	--	FIXME: addon?
	for i, embedName in pairs(embeds) do
		local embedTree = Tree:Get(embedName)
		embedTree:PrepareForLoD()
		
		--[[
		if embed then
			if embed:CanLoD() then
				--	don't load it now but make sure its dependencies are prepared
				self:PrepareLoDTree(embedName)
			else
				--	FIXME: if it's already loaded
				--	force-load it now so we can load the parent on demand
				self:ForceLoadTree(depName)
			end
		end
		]]
	end
end


--	load the root too, since it may actually be a leaf
function tree:ForceLoad()
	--	load dependencies
	local dependencies = {self.root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local depTree = Tree:Get(depName)
		depTree:ForceLoad()
	end

	--	load embeds, if they are available separately
	local embeds = {self.root:GetEmbeds()}
	for i, embedName in pairs(embeds) do
		local embedTree = Tree:Get(embedName)
		embedTree:ForceLoad()
	end
	
	root:ForceLoad()
end


--[[	don't think i need this
function core:LoadLoDTree(root)
	root = self:GetAddon(root)
	assert(root)
	
	--	load dependencies
	local dependencies = {root:GetDependencies(addon)}
	for i, depName in pairs(dependencies) do
		self:LoadLoDTree(depName)
	end

	--	load embeds, if they are available separately
	local embeds = {root:GetEmbeds(addon)}
	for i, embedName in pairs(embeds) do
		if Addon:Exists(embedName) then
			self:LoadLoDTree(embedName)
		end
	end
	
	root:LoD()
end
]]