view DependencyLoader/Tree.lua @ 15:a46bf694050c

cleaned up Tree's methods a bit and improved documentation Addon:Exists will now return false for Blizzard addons (needs to be handled better) Addon.lua will now use the raw hooks from the interface module fixed the inverted returns from IsForceLoadAvailable EnableAddOn and LoadAddOn hooks will now skip the extra processing if the addon does not exist or is a Blizzard addon moved the EnableAddOn queing to the interface
author mckenziemc
date Sat, 11 Dec 2010 01:48:39 -0800
parents b230b94d4487
children e7995d599184
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")


local classes = addonTable.classes


Tree.trees = {}


---	(private) Creates a new tree object.
--	Assumptions: root addon exists and is not a Blizzard addon.
--	@param	root	Name, index, or Addon object of the root addon.
function Tree:New(root)
	if type(root) ~= "table" then
		root = 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
--	Assumptions: root addon exists and is not a Blizzard addon
--	@param	root	Name, index, or Addon object of the root.
function Tree:Get(root)
	if type(root) ~= "table" then
		root = 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


--	NOTE: All tree:Can functions should check the root too.
--	That way, the hooks don't have to do "if addon:Can_ and tree:Can_", 
--	plus the tree can cache info that includes the capabilities of the 
--	root addon.


---	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()
	local root = self.root
	
	if root:IsLoaded() then
		return true
	end
	
	if not root:CanLoad() then
		return false
	end

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


---	Checks if the tree can be loaded on demand.
--	Does not allow for force-loading; dependencies must 
--	already be loaded, or enabled and LoD.
function tree:CanLoD()
	local root = self.root
	
	if root:IsLoaded() then
		return true
	end
	
	if not root:CanLoD() then
		return false
	end
	
	--	now check dependencies recursively
	local dependencies = {root:GetDependencies()}
	
	for i, depName in pairs(dependencies) do
		if not classes.Addon:Exists(depName) then
			return false
		end
		
		local dep = classes.Addon:Get(depName)
		local depTree = Tree:Get(dep)
		
		if not depTree:CanLoD() then
			return false
		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()
	local root = self.root
	--	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 root:IsLoaded() then
		return true
	end
	
	if not root:CanForceLoad() then
		return false
	end
	
	--	now check dependencies recursively
	local dependencies = {root:GetDependencies()}
	
	for i, depName in pairs(dependencies) do
		if not classes.Addon:Exists(depName) then
			return false
		end
		
		local dep = classes.Addon:Get(depName)
		local depTree = Tree:Get(dep)
		
		if not depTree:CanForceLoad() then
			return false
		end
	end
	
	return true
end


---	Prepares this tree to be loaded, whether through 
--	a ui reload or by loading on demand.
--	Assumptions: CanLoad is true for this tree
function tree:PrepareForLoad()
	local root = self.root
	
	--	The Addon class will take care of delaying enables 
	--	till after PLAYER_LOGIN if necessary.
	
	root:Enable()
	
	--	enable dependencies
	local dependencies = {root:GetDependencies()}
	
	for i, depName in pairs(dependencies) do
		Tree:Get(depName):PrepareForLoad()
	end

	--	prepare embeds, if they are available separately
	local embeds = {root:GetEmbeds(addon)}
	
	for i, embedName in pairs(embeds) do
		if classes.Addon:Exists(embedName) then
			Tree:Get(embedName):PrepareForLoad()
		end
	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

---	Force-loads this addon tree. If a subtree can be loaded on 
--	demand, this function will enable but not load the subtree.
--	Assumptions: the root addon should be force-loaded, not just enabled.
--function tree:ForceLoad()

--	Assumptions: the root is loadable-on demand, and force-load is available.
--	and tree can be force-loaded
function tree:PrepareForLoD()
	local root = self.root
	
	--	prepare dependencies first (for consistency)
	local dependencies = {root:GetDependencies()}
	
	for i, depName in pairs(dependencies) do
		local dep = classes.Addon:Get(depName)
		
		if not dep:IsLoaded() then
			if dep:CanLoD() then
				--	don't load it now but make sure its dependencies are prepared
				Tree:Get(dep):PrepareForLoD()
			else
				--	Based on our assumption about the tree, this addon 
				--	should be able to force-load.
				--	force-load it now so we can load the parent on demand
				Tree:Get(dep):ForceLoad()
			end
		end
	end

	--	prepare embeds, if they are available separately
	local embeds = {root:GetEmbeds()}
	
	for i, embedName in pairs(embeds) do
		if classes.Addon:Exists(embedName) then
			local embed = classes.Addon:Get(embedName)
			
			if not embed:IsLoaded() then
				if embed:CanLoD() then
					Tree:Get(embed):PrepareForLoD()
				else
					Tree:Get(embed):ForceLoad()
				end
			end
		end
	end

	root:Enable()
end


---	Force-loads this tree.
--	This will also load any LoD addons in the tree
function tree:ForceLoad()
	local root = self.root
	
	--	FIXME: if already loaded

	--	load dependencies
	local dependencies = {root:GetDependencies()}
	
	for i, depName in pairs(dependencies) do
		Tree:Get(depName):ForceLoad()
	end

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