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@10: -- FIXME: prevent infinite loops in the recursive functions mckenziemc@8: mckenziemc@8: mckenziemc@8: local Tree, tree = addonTable:NewClass("Tree") mckenziemc@8: mckenziemc@8: mckenziemc@15: local classes = addonTable.classes mckenziemc@15: mckenziemc@15: mckenziemc@8: Tree.trees = {} mckenziemc@8: mckenziemc@10: mckenziemc@15: --- (private) Creates a new tree object. mckenziemc@15: -- Assumptions: root addon exists and is not a Blizzard addon. 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@15: root = 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@15: -- Assumptions: root addon exists and is not a Blizzard 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@15: root = 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@15: -- NOTE: All tree:Can functions should check the root too. mckenziemc@15: -- That way, the hooks don't have to do "if addon:Can_ and tree:Can_", mckenziemc@15: -- plus the tree can cache info that includes the capabilities of the mckenziemc@15: -- root addon. mckenziemc@15: mckenziemc@15: mckenziemc@15: --- Checks if the tree rooted at the specified addon mckenziemc@12: -- can be loaded if all dependencies are enabled mckenziemc@12: -- and the UI is reloaded. mckenziemc@12: -- Basically makes sure all dependencies are installed. mckenziemc@12: function tree:CanLoad() mckenziemc@15: local root = self.root mckenziemc@15: mckenziemc@15: if root:IsLoaded() then mckenziemc@15: return true mckenziemc@15: end mckenziemc@15: mckenziemc@15: if not root:CanLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@12: -- check all the dependencies mckenziemc@15: local dependencies = {root:GetDependencies()} mckenziemc@15: mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@15: if not classes.Addon:Exists(depName) then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@15: local dep = classes.Addon:Get(depName) mckenziemc@15: local depTree = Tree:Get(dep) mckenziemc@8: mckenziemc@12: if not depTree:CanLoad() then mckenziemc@15: return false mckenziemc@10: end mckenziemc@10: end mckenziemc@10: mckenziemc@12: return true mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@12: --- Checks if the tree can be loaded on demand. mckenziemc@15: -- Does not allow for force-loading; dependencies must mckenziemc@15: -- already be loaded, or enabled and LoD. mckenziemc@10: function tree:CanLoD() mckenziemc@15: local root = self.root mckenziemc@15: mckenziemc@15: if root:IsLoaded() then mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@15: if not root:CanLoD() then mckenziemc@8: return false mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- now check dependencies recursively mckenziemc@15: local dependencies = {root:GetDependencies()} mckenziemc@15: mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@15: if not classes.Addon:Exists(depName) then mckenziemc@15: return false mckenziemc@15: end mckenziemc@15: mckenziemc@15: local dep = classes.Addon:Get(depName) mckenziemc@15: local depTree = Tree:Get(dep) mckenziemc@8: mckenziemc@10: if not depTree:CanLoD() then mckenziemc@10: return false mckenziemc@8: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@15: --- Checks if this tree can be force-loaded. mckenziemc@15: -- Does not check user settings nor if force-loading is actually available. mckenziemc@15: -- @return canForceLoad True if this tree can be force loaded, false otherwise mckenziemc@15: function tree:CanForceLoad() mckenziemc@15: local root = self.root mckenziemc@15: -- TODO: remove redundencies (for now they're here for design flexibility) mckenziemc@15: mckenziemc@15: -- this addon must be loaded, able to load-on-demand, or able to force-load mckenziemc@15: if root:IsLoaded() then mckenziemc@15: return true mckenziemc@15: end mckenziemc@15: mckenziemc@15: if not root:CanForceLoad() then mckenziemc@12: return false mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- now check dependencies recursively mckenziemc@15: local dependencies = {root:GetDependencies()} mckenziemc@15: mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@15: if not classes.Addon:Exists(depName) then mckenziemc@12: return false mckenziemc@8: end mckenziemc@12: mckenziemc@15: local dep = classes.Addon:Get(depName) mckenziemc@15: local depTree = Tree:Get(dep) mckenziemc@15: mckenziemc@15: if not depTree:CanForceLoad() then mckenziemc@15: return false mckenziemc@12: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@12: mckenziemc@15: --- Prepares this tree to be loaded, whether through mckenziemc@15: -- a ui reload or by loading on demand. mckenziemc@15: -- Assumptions: CanLoad is true for this tree mckenziemc@15: function tree:PrepareForLoad() mckenziemc@15: local root = self.root mckenziemc@15: mckenziemc@15: -- The Addon class will take care of delaying enables mckenziemc@15: -- till after PLAYER_LOGIN if necessary. mckenziemc@15: mckenziemc@15: root:Enable() mckenziemc@15: mckenziemc@15: -- enable dependencies mckenziemc@15: local dependencies = {root:GetDependencies()} mckenziemc@15: mckenziemc@15: for i, depName in pairs(dependencies) do mckenziemc@15: Tree:Get(depName):PrepareForLoad() mckenziemc@15: end mckenziemc@12: mckenziemc@15: -- prepare embeds, if they are available separately mckenziemc@15: local embeds = {root:GetEmbeds(addon)} mckenziemc@12: mckenziemc@15: for i, embedName in pairs(embeds) do mckenziemc@15: if classes.Addon:Exists(embedName) then mckenziemc@15: Tree:Get(embedName):PrepareForLoad() mckenziemc@12: end mckenziemc@12: end mckenziemc@12: end mckenziemc@12: mckenziemc@12: mckenziemc@12: -- I think the problem this solves is a major issue with mckenziemc@12: -- migrating to separate libs. think about it more and document mckenziemc@12: -- here and in project description mckenziemc@15: mckenziemc@15: --- Force-loads this addon tree. If a subtree can be loaded on mckenziemc@15: -- demand, this function will enable but not load the subtree. mckenziemc@15: -- Assumptions: the root addon should be force-loaded, not just enabled. mckenziemc@15: --function tree:ForceLoad() mckenziemc@15: mckenziemc@15: -- Assumptions: the root is loadable-on demand, and force-load is available. mckenziemc@15: -- and tree can be force-loaded mckenziemc@12: function tree:PrepareForLoD() mckenziemc@15: local root = self.root mckenziemc@12: mckenziemc@15: -- prepare dependencies first (for consistency) mckenziemc@15: local dependencies = {root:GetDependencies()} mckenziemc@15: mckenziemc@12: for i, depName in pairs(dependencies) do mckenziemc@15: local dep = classes.Addon:Get(depName) mckenziemc@12: mckenziemc@15: if not dep:IsLoaded() then mckenziemc@15: if dep:CanLoD() then mckenziemc@15: -- don't load it now but make sure its dependencies are prepared mckenziemc@15: Tree:Get(dep):PrepareForLoD() mckenziemc@15: else mckenziemc@15: -- Based on our assumption about the tree, this addon mckenziemc@15: -- should be able to force-load. mckenziemc@15: -- force-load it now so we can load the parent on demand mckenziemc@15: Tree:Get(dep):ForceLoad() mckenziemc@15: end mckenziemc@12: end mckenziemc@12: end mckenziemc@12: mckenziemc@12: -- prepare embeds, if they are available separately mckenziemc@15: local embeds = {root:GetEmbeds()} mckenziemc@15: mckenziemc@12: for i, embedName in pairs(embeds) do mckenziemc@15: if classes.Addon:Exists(embedName) then mckenziemc@15: local embed = classes.Addon:Get(embedName) mckenziemc@15: mckenziemc@15: if not embed:IsLoaded() then mckenziemc@15: if embed:CanLoD() then mckenziemc@15: Tree:Get(embed):PrepareForLoD() mckenziemc@15: else mckenziemc@15: Tree:Get(embed):ForceLoad() mckenziemc@15: end mckenziemc@12: end mckenziemc@12: end mckenziemc@12: end mckenziemc@15: mckenziemc@15: root:Enable() mckenziemc@12: end mckenziemc@12: mckenziemc@12: mckenziemc@15: --- Force-loads this tree. mckenziemc@15: -- This will also load any LoD addons in the tree mckenziemc@10: function tree:ForceLoad() mckenziemc@15: local root = self.root mckenziemc@15: mckenziemc@15: -- FIXME: if already loaded mckenziemc@15: mckenziemc@8: -- load dependencies mckenziemc@15: local dependencies = {root:GetDependencies()} mckenziemc@15: mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@15: Tree:Get(depName):ForceLoad() mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- load embeds, if they are available separately mckenziemc@15: local embeds = {root:GetEmbeds()} mckenziemc@15: mckenziemc@8: for i, embedName in pairs(embeds) do mckenziemc@15: if classes.Addon:Exists(embedName) then mckenziemc@15: Tree:Get(embedName):ForceLoad() mckenziemc@15: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: root:ForceLoad() mckenziemc@8: end