mckenziemc@8: -- Tree mckenziemc@8: -- Represents a recursive "tree" of addon mckenziemc@8: -- dependencies rooted at a specific addon. mckenziemc@8: mckenziemc@8: mckenziemc@8: local addonName, addonTable = ... mckenziemc@8: mckenziemc@8: mckenziemc@8: -- TODO: prevent infinite loops in the recursive functions mckenziemc@8: mckenziemc@8: mckenziemc@8: local Tree, tree = addonTable:NewClass("Tree") mckenziemc@8: mckenziemc@8: mckenziemc@8: Tree.trees = {} mckenziemc@8: mckenziemc@8: -- internal mckenziemc@8: -- Creates a new tree object mckenziemc@8: -- @param root Name, index, or Addon object of the root addon. mckenziemc@8: function Tree:New(root) mckenziemc@8: if type(root) ~= "table" then mckenziemc@8: root = addonTable.classes.Addon:Get(root) mckenziemc@8: end mckenziemc@8: mckenziemc@8: local instance = {} mckenziemc@8: setmetatable(instance, self.instanceMetatable) mckenziemc@8: mckenziemc@8: instance.root = root mckenziemc@8: mckenziemc@8: return instance mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: --- Retrieves the tree rooted at the specified addon mckenziemc@8: -- @param root Name, index, or Addon object of the root. mckenziemc@8: function Tree:Get(root) mckenziemc@8: if type(root) ~= "table" then mckenziemc@8: root = addonTable.classes.Addon:Get(root) mckenziemc@8: end mckenziemc@8: mckenziemc@8: local tree = self.trees[root] mckenziemc@8: mckenziemc@8: if tree then mckenziemc@8: return tree mckenziemc@8: else mckenziemc@8: tree = self:New(root) mckenziemc@8: self.trees[root] = tree mckenziemc@8: return tree mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: function tree:GetLoadBitfield() mckenziemc@8: local bitfield = self.root:GetLoadBitfield() mckenziemc@8: mckenziemc@8: local dependencies = {self.root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@8: local depTree = Tree:Get(depName) mckenziemc@8: mckenziemc@8: if depTree == nil then mckenziemc@8: return 0 mckenziemc@8: end mckenziemc@8: mckenziemc@8: bitfield = bit.band(bitfield, depTree:GetLoadBitfield()) mckenziemc@8: end mckenziemc@8: mckenziemc@8: return bitfield mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: -- Checks if the tree rooted at the specified addon can be force-loaded. mckenziemc@8: -- @param root The name or index of the root addon. mckenziemc@8: function core:CanForceLoadTree(root) mckenziemc@8: -- TODO: if some addons have already loaded, we have to check mckenziemc@8: -- forceafter for those and forcebefore for the others mckenziemc@8: return false mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: -- NOTE: any tree that can be loaded on demand is also eligible for force-loading mckenziemc@8: -- Checks if the tree rooted at the specified addon can be loaded on demand. mckenziemc@8: -- @param root The name or index of the root addon. mckenziemc@8: function core:CanLoDTree(root) mckenziemc@8: local bitfield = self:GetLoadBitfield() mckenziemc@8: root = self:GetAddon(root) mckenziemc@8: assert(root) mckenziemc@8: mckenziemc@8: -- since this will be used recursively, return true if mckenziemc@8: -- this is already loaded. mckenziemc@8: if root:IsLoaded() then mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- true if all dependencies are loaded or LoD mckenziemc@8: mckenziemc@8: if not root:CanLoD() then mckenziemc@8: return false mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- now check dependencies recursively mckenziemc@8: local dependencies = {root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@8: local dep = self:GetAddon(depName) mckenziemc@8: mckenziemc@8: if not dep then mckenziemc@8: return false -- missing mckenziemc@8: end mckenziemc@8: mckenziemc@8: if not dep:IsLoaded() then mckenziemc@8: if not self:CanLoDTree(depName) then mckenziemc@8: return false mckenziemc@8: end mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: -- Checks if the tree rooted at the specified addon mckenziemc@8: -- can be loaded if all dependencies are enabled mckenziemc@8: -- the UI is reloaded. mckenziemc@8: -- Basically makes sure all dependencies are installed. mckenziemc@8: function core:CanReloadTree(root) mckenziemc@8: -- convert root to an Addon object mckenziemc@8: root = self:GetAddon(root) mckenziemc@8: assert(root) mckenziemc@8: mckenziemc@8: if root:IsLoaded() then mckenziemc@8: return true mckenziemc@8: -- FIXME: deps may have been disabled mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- TODO: make sure the root isn't incompatible mckenziemc@8: mckenziemc@8: -- now check dependencies recursively mckenziemc@8: -- FIXME: prevent infinite recursion mckenziemc@8: mckenziemc@8: local dependencies = {root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@8: local dep = self:GetAddon(depName) mckenziemc@8: mckenziemc@8: if dep == nil then mckenziemc@8: return false -- missing dependency mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- TODO: make sure it's compatible mckenziemc@8: end mckenziemc@8: mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- Loads the tree rooted at the specified addon. mckenziemc@8: -- FIXME: load the root too or not? mckenziemc@8: -- Supports both LoD addons and those that require force-loading. mckenziemc@8: function core:LoadTree(addon) mckenziemc@8: -- don't check if the tree can actually be loaded. mckenziemc@8: -- external code should do that itself to check if it mckenziemc@8: -- should even call this at all. mckenziemc@8: mckenziemc@8: if self:ForceLoadAvailable() then mckenziemc@8: -- LoD trees can also be force-loaded mckenziemc@8: self:ForceLoadTree(addon) mckenziemc@8: else mckenziemc@8: self:LoadLoDTree(addon) mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: -- load the root too, since it may actually be a leaf mckenziemc@8: function core:ForceLoadTree(root) mckenziemc@8: root = self:GetAddon(root) mckenziemc@8: assert(root) mckenziemc@8: mckenziemc@8: -- load dependencies mckenziemc@8: local dependencies = {root:GetDependencies(addon)} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@8: self:ForceLoadTree(depName) mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- load embeds, if they are available separately mckenziemc@8: local embeds = {root:GetEmbeds(addon)} mckenziemc@8: for i, embedName in pairs(embeds) do mckenziemc@8: if Addon:Exists(embedName) then mckenziemc@8: self:ForceLoadTree(embedName) mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: root:ForceLoad() mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: function core:LoadLoDTree(root) mckenziemc@8: root = self:GetAddon(root) mckenziemc@8: assert(root) mckenziemc@8: mckenziemc@8: -- load dependencies mckenziemc@8: local dependencies = {root:GetDependencies(addon)} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@8: self:LoadLoDTree(depName) mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- load embeds, if they are available separately mckenziemc@8: local embeds = {root:GetEmbeds(addon)} mckenziemc@8: for i, embedName in pairs(embeds) do mckenziemc@8: if Addon:Exists(embedName) then mckenziemc@8: self:LoadLoDTree(embedName) mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: root:LoD() mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: -- I think the problem this solves is a major issue with mckenziemc@8: -- migrating to separate libs. think about it more and document mckenziemc@8: -- here and in project description mckenziemc@8: function core:PrepareLoDTree(root) mckenziemc@8: root = self:GetAddon(root) mckenziemc@8: assert(root) mckenziemc@8: mckenziemc@8: -- assume root is LoD mckenziemc@8: mckenziemc@8: -- check dependencies mckenziemc@8: local dependencies = {root:GetDependencies(addon)} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@8: local dep = self:GetAddon(depName) mckenziemc@8: mckenziemc@8: -- assume external code made sure it exists mckenziemc@8: mckenziemc@8: if dep:CanLoD() then mckenziemc@8: -- don't load it now but make sure its dependencies are prepared mckenziemc@8: self:PrepareLoDTree(depName) mckenziemc@8: else mckenziemc@8: -- FIXME: if it's already loaded mckenziemc@8: -- force-load it now so we can load the parent on demand mckenziemc@8: self:ForceLoadTree(depName) mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- prepare embeds, if they are available separately mckenziemc@8: local embeds = {root:GetEmbeds(addon)} -- FIXME: addon? mckenziemc@8: for i, embedName in pairs(embeds) do mckenziemc@8: local embed = self:GetAddon(embedName) mckenziemc@8: mckenziemc@8: if embed then mckenziemc@8: if embed:CanLoD() then mckenziemc@8: -- don't load it now but make sure its dependencies are prepared mckenziemc@8: self:PrepareLoDTree(embedName) mckenziemc@8: else mckenziemc@8: -- FIXME: if it's already loaded mckenziemc@8: -- force-load it now so we can load the parent on demand mckenziemc@8: self:ForceLoadTree(depName) mckenziemc@8: end mckenziemc@8: end mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: function Core:PrepareReloadTree(addon) mckenziemc@8: root = self:GetAddon(root) mckenziemc@8: assert(root) mckenziemc@8: mckenziemc@8: root:Enable() mckenziemc@8: mckenziemc@8: -- check dependencies mckenziemc@8: local dependencies = {root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@8: self:PrepareReloadTree(depName) mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- prepare embeds, if they are available separately mckenziemc@8: local embeds = {root:GetEmbeds(addon)} mckenziemc@8: for i, embedName in pairs(embeds) do mckenziemc@8: local embed = self:GetAddon(embedName) mckenziemc@8: mckenziemc@8: if embed then mckenziemc@8: self:PrepareReloadTree(embedName) mckenziemc@8: end mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@8: function Core:ForceLoadAvailable() mckenziemc@8: return true mckenziemc@8: -- FIXME: use field and a frame registered for PLAYER_LOGIN mckenziemc@8: end