view DependencyLoader/Tree.lua @ 9:5362e308c3eb

renamed the old DependencyLoader module to DependencyLoader_Bootstrap renamed DependencyLoader_Core to DependencyLoader
author mckenziemc
date Sun, 05 Dec 2010 00:12:57 -0800
parents DependencyLoader_Core/Tree.lua@930871e163bc
children e0a4a8b5b389
line wrap: on
line source
--	Tree
--	Represents a recursive "tree" of addon 
--	dependencies rooted at a specific addon.


local addonName, addonTable = ...


--	TODO: 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


function tree:GetLoadBitfield()
	local bitfield = self.root:GetLoadBitfield()
	
	local dependencies = {self.root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local depTree = Tree:Get(depName)
		
		if depTree == nil then
			return 0
		end
		
		bitfield = bit.band(bitfield, depTree:GetLoadBitfield())
	end
	
	return bitfield
end


--	Checks if the tree rooted at the specified addon can be force-loaded.
--	@param	root	The name or index of the root addon.
function core:CanForceLoadTree(root)
	--	TODO: if some addons have already loaded, we have to check 
	--	forceafter for those and forcebefore for the others
	return false
end


--	NOTE: any tree that can be loaded on demand is also eligible for force-loading
--	Checks if the tree rooted at the specified addon can be loaded on demand.
--	@param	root	The name or index of the root addon.
function core:CanLoDTree(root)
	local bitfield = self:GetLoadBitfield()
	root = self:GetAddon(root)
	assert(root)

	--	since this will be used recursively, return true if 
	--	this is already loaded.
	if root:IsLoaded() then
		return true
	end
	
	--	true if all dependencies are loaded or LoD
	
	if not root:CanLoD() then
		return false
	end
	
	--	now check dependencies recursively
	local dependencies = {root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local dep = self:GetAddon(depName)
		
		if not dep then
			return false		--	missing
		end
		
		if not dep:IsLoaded() then
			if not self:CanLoDTree(depName) then
				return false
			end
		end
	end
	
	return true
end


--	Checks if the tree rooted at the specified addon 
--	can be loaded if all dependencies are enabled 
--	the UI is reloaded.
--	Basically makes sure all dependencies are installed.
function core:CanReloadTree(root)
	--	convert root to an Addon object
	root = self:GetAddon(root)
	assert(root)
	
	if root:IsLoaded() then
		return true
		--	FIXME: 	deps may have been disabled
	end
	
	--	TODO: make sure the root isn't incompatible
	
	--	now check dependencies recursively
	--	FIXME: prevent infinite recursion

	local dependencies = {root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		local dep = self:GetAddon(depName)
		
		if dep == nil then
			return false		--	missing dependency
		end

		--	TODO: make sure it's compatible
	end
	
	return true
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 core:LoadTree(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


--	load the root too, since it may actually be a leaf
function core:ForceLoadTree(root)
	root = self:GetAddon(root)
	assert(root)
	
	--	load dependencies
	local dependencies = {root:GetDependencies(addon)}
	for i, depName in pairs(dependencies) do
		self:ForceLoadTree(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:ForceLoadTree(embedName)
		end
	end
	
	root:ForceLoad()
end


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


--	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 core:PrepareLoDTree(root)
	root = self:GetAddon(root)
	assert(root)
	
	--	assume root is LoD
	
	--	check dependencies
	local dependencies = {root:GetDependencies(addon)}
	for i, depName in pairs(dependencies) do
		local dep = self:GetAddon(depName)
		
		--	assume external code made sure it exists
		
		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 = {root:GetEmbeds(addon)}	--	FIXME: addon?
	for i, embedName in pairs(embeds) do
		local embed = self:GetAddon(embedName)
		
		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


function Core:PrepareReloadTree(addon)
	root = self:GetAddon(root)
	assert(root)
	
	root:Enable()
	
	--	check dependencies
	local dependencies = {root:GetDependencies()}
	for i, depName in pairs(dependencies) do
		self:PrepareReloadTree(depName)
	end

	--	prepare embeds, if they are available separately
	local embeds = {root:GetEmbeds(addon)}
	for i, embedName in pairs(embeds) do
		local embed = self:GetAddon(embedName)
		
		if embed then
			self:PrepareReloadTree(embedName)
		end
	end
end


function Core:ForceLoadAvailable()
	return true
	--	FIXME: use field and a frame registered for PLAYER_LOGIN
end