changeset 8:930871e163bc

created a new Tree class and started rearranging code
author mckenziemc
date Sat, 04 Dec 2010 23:05:34 -0800
parents f4ab9d7b97b0
children 5362e308c3eb
files DependencyLoader_Core/Addon.lua DependencyLoader_Core/Core.lua DependencyLoader_Core/Tree.lua DependencyLoader_Core/class.lua
diffstat 4 files changed, 393 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/DependencyLoader_Core/Addon.lua	Sat Dec 04 14:45:36 2010 -0800
+++ b/DependencyLoader_Core/Addon.lua	Sat Dec 04 23:05:34 2010 -0800
@@ -4,7 +4,6 @@
 
 local addonName, addonTable = ...
 
-print("running Addon.lua")
 
 --	NOTE: I assume that the API addon functions are 
 --	slightly quicker with an index than with a number.
@@ -13,11 +12,25 @@
 
 local Addon, addon = addonTable:NewClass("Addon")
 
+
+--	load ability masks
+Addon.loadMasks = {
+	reload = 		bit.lshift(1, 0),	--	can load after reloadui
+	ondemand = 		bit.lshift(1, 1),	--	can load on demand
+	forceafter = 	bit.lshift(1, 2),	--	force load after it would normally be loaded
+	forcebefore = 	bit.lshift(1, 3),	--	force load before it would normally be loaded
+}
+
+
+Addon.addons = {}
+Addon.nameToIndex = {}
+
+
+--	Internal function
+--	Creates a new Addon object
+--	@param	id		Name or index of the addon.
 function Addon:New(id)
-	assert(type(id) == "number" or type(id) == "string")
-
 	local instance = {}
-	
 	setmetatable(instance, self.instanceMetatable)
 	
 	if type(id) == "number" then
@@ -47,6 +60,37 @@
 end
 
 
+---	Retrieves an Addon object.
+--	@param	id	Name or index of the addon to retrieve.
+--	@return 	The Addon object, or nil if not found.
+function Addon:Get(id)
+	if not self:Exists(id) then
+		return nil
+	end
+	
+	if type(id) == "number" then
+		if self.addons[id] ~= nil then
+			return self.addons[id]
+		end
+		
+		local new = self:New(id)
+		self.addons[new:GetIndex()] = new
+		self.nameToIndex[new:GetName()] = id
+		
+		return new
+	elseif type(id) == "string" then
+		if self.nameToIndex[id] then
+			return self.addons[self.nameToIndex[id] ]
+		end
+		
+		local new = self:New(id)
+		self.addons[new:GetIndex()] = new
+		self.nameToIndex[id] = new:GetIndex()
+		
+		return new
+	end
+end	
+
 --	Checks if an addon exists with the specified name.
 --	@param	addon	Name of the addon.
 --	@return			True if the addon is present, false otherwise.
@@ -71,6 +115,11 @@
 end
 
 
+function Addon:GetLoadMasks()
+	return self.masks
+end
+
+
 function addon:GetName()
 	return self.name
 end
@@ -92,13 +141,39 @@
 	end
 end
 
---	FIXME: an addon may be present but unloadable if loading out of date addons is disabled.
---	NOTE: CanForceLoad and CanLoD don't check the status of dependencies
 
-function addon:CanForceLoad()
-	return true		--	TODO: check if there's any reason addons can't be forceloaded
+function addon:GetLoadBitfield()
+	local bitfield = 0
+	
+	if self:CanLoad() then
+		bitfield = bitfield + self.masks.reload
+	end
+	
+	if self:CanLoD() then
+		bitfield = bitfield + self.masks.ondemand
+	end
+	
+	if self:CanForceLoadAfter() then
+		bitfield = bitfield + self.masks.forceafter
+	end
+	
+	if self:CanForceLoadBefore() then
+		bitfield = bitfield + self.masks.forcebefore
+	end
+	
+	return bitfield
 end
 
+
+---	Checks if the addon is loadable.
+--	Considers interface issues, missing, etc. (NYI)
+--	Does not check dependencies.
+function addon:CanLoad()
+	--	FIXME: an addon may be present but unloadable if loading out of date addons is disabled.
+	return true
+end
+
+
 function addon:CanLoD()
 	--	FIXME: what will the client say about addons using LoadManagers if the LM was force-loaded?
 	if IsAddOnLoadOnDemand(self.name) then
@@ -108,6 +183,25 @@
 	end
 end
 
+
+function addon:CanForceLoadAfter()
+	--	TODO: check Errata module
+	return false
+end
+
+
+function addon:CanForceLoadBefore()
+	--	TODO: check Errata module
+	return false		--	TODO: check if there's any reason addons can't be forceloaded
+end
+
+
+function addon:Enable()
+	--	FIXME: delay this till after PLAYER_LOGIN or it'll get force-loaded
+	EnableAddOn(self.name)
+end
+
+
 --	NOTE: only call for LoD, not force-loading
 function addon:Load()
 	assert(self:CanLoD())
@@ -116,6 +210,7 @@
 	LoadAddOn(self.name)
 end
 
+
 function addon:ForceLoad()
 	assert(self:CanForceLoad())
 	--	TODO: make sure force-loading is available at this time
@@ -125,6 +220,7 @@
 
 
 function addon:GetDependencies()
+	--	TODO: consider no-lib embeds as deps?
 	return GetAddOnDependencies(self.index)
 end
 
--- a/DependencyLoader_Core/Core.lua	Sat Dec 04 14:45:36 2010 -0800
+++ b/DependencyLoader_Core/Core.lua	Sat Dec 04 23:05:34 2010 -0800
@@ -1,16 +1,11 @@
+
 
 
 local addonName, addonTable = ...
 
 
-print("running Core.lua")
-
---	TODO: prevent infinite loops in the recursive functions
-
-
 local Core, core = addonTable:NewClass("Core")
 
-local Addon = addonTable.classes.Addon
 
 function Core:New()
 	local instance = {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DependencyLoader_Core/Tree.lua	Sat Dec 04 23:05:34 2010 -0800
@@ -0,0 +1,287 @@
+--	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
--- a/DependencyLoader_Core/class.lua	Sat Dec 04 14:45:36 2010 -0800
+++ b/DependencyLoader_Core/class.lua	Sat Dec 04 23:05:34 2010 -0800
@@ -4,6 +4,7 @@
 
 local addonName, addonTable = ...
 
+--	FIXME: prevent duplicate class definitions
 
 print( string.format([[running %s\class.lua]], addonName) )