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@8: Tree.trees = {} mckenziemc@8: mckenziemc@10: 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@12: -- 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@12: if not self.root:CanLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@12: -- check all the dependencies mckenziemc@8: local dependencies = {self.root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@12: local dep = addonTable.classes.Addon:Get(depName) mckenziemc@12: mckenziemc@12: if not dep:CanLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@8: local depTree = Tree:Get(depName) mckenziemc@8: mckenziemc@12: if not depTree:CanLoad() then mckenziemc@12: return false -- missing dependency 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@12: -- Does not consider force-loading; dependencies must mckenziemc@12: -- be enabled and LoD, or loaded mckenziemc@10: function tree:CanLoD() mckenziemc@8: -- since this will be used recursively, return true if mckenziemc@8: -- this is already loaded. mckenziemc@10: if self.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@10: if not self.root:CanLoD() then mckenziemc@8: return false mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- now check dependencies recursively mckenziemc@12: local dependencies = {self.root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@10: local depTree = Tree:Get(depName) 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@12: --- Checks if this tree can be loaded on demand as long as mckenziemc@12: -- force-loading is allowed for preparing dependencies. mckenziemc@12: function tree:CanLoDWithForce() mckenziemc@12: if not self.root:CanLoD() then mckenziemc@12: return false mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- now check dependencies recursively mckenziemc@10: local dependencies = {self.root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@12: local dep = addonTable.classes.Addon:Get(depName) mckenziemc@10: local depTree = Tree:Get(depName) mckenziemc@8: mckenziemc@12: if not dep:CanLoad() then mckenziemc@12: return false mckenziemc@8: end mckenziemc@12: mckenziemc@12: if dep:CanLoD() then mckenziemc@12: if not depTree:CanLoDWithForce() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: elseif dep:CanForceLoad() then mckenziemc@12: if not depTree:CanForceLoad() then mckenziemc@12: return mckenziemc@12: end mckenziemc@12: end mckenziemc@8: end mckenziemc@8: mckenziemc@8: return true mckenziemc@8: end mckenziemc@8: mckenziemc@12: mckenziemc@12: mckenziemc@12: --- Checks if this tree can be force-loaded. mckenziemc@12: -- Does not check user settings nor if force-loading is actually available. mckenziemc@12: -- @return canForceLoad True if this tree can be force loaded, false otherwise mckenziemc@12: function tree:CanForceLoad() mckenziemc@12: -- TODO: remove redundencies (for now they're here for design flexibility) mckenziemc@12: mckenziemc@12: -- this addon must be loaded, able to load-on-demand, or able to force-load mckenziemc@12: if self.root:IsLoaded() then mckenziemc@12: return true mckenziemc@12: end mckenziemc@12: mckenziemc@12: if not self.root:CanLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@12: if not self.root:CanForceLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@12: -- now check dependencies recursively mckenziemc@12: local dependencies = {self.root:GetDependencies()} mckenziemc@12: for i, depName in pairs(dependencies) do mckenziemc@12: local dep = addonTable.classes.Addon:Get(depName) mckenziemc@12: local depTree = Tree:Get(depName) mckenziemc@12: mckenziemc@12: if not dep:CanLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@12: if not dep:CanLoD() and not dep:CanForceLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: mckenziemc@12: if not depTree:CanForceLoad() then mckenziemc@12: return false mckenziemc@12: end mckenziemc@12: end mckenziemc@12: mckenziemc@12: return mckenziemc@12: end mckenziemc@12: mckenziemc@12: mckenziemc@12: mckenziemc@10: --[[ 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@10: function tree:Load(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@10: ]] mckenziemc@8: mckenziemc@8: mckenziemc@12: function tree:PrepareForReload() mckenziemc@12: -- if force-loading is enabled then we have to queue this if the addon isn't lod mckenziemc@12: local function callback() mckenziemc@12: self.root:Enable() mckenziemc@12: mckenziemc@12: -- check dependencies mckenziemc@12: local dependencies = {self.root:GetDependencies()} mckenziemc@12: for i, depName in pairs(dependencies) do mckenziemc@12: Tree:Get(depName):PrepareForReload() mckenziemc@12: end mckenziemc@12: mckenziemc@12: -- prepare embeds, if they are available separately mckenziemc@12: local embeds = {self.root:GetEmbeds(addon)} mckenziemc@12: for i, embedName in pairs(embeds) do mckenziemc@12: Tree:Get(embedName):PrepareForReload() mckenziemc@12: end mckenziemc@12: end mckenziemc@12: mckenziemc@12: if IsLoggedIn() then mckenziemc@12: callback() mckenziemc@12: else mckenziemc@12: -- TODO: replace with cleaner alternative? mckenziemc@12: local frame = CreateFrame("Frame") mckenziemc@12: frame:SetScript("OnEvent", callback) mckenziemc@12: frame:RegisterEvent("PLAYER_LOGIN") 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@12: function tree:PrepareForLoD() mckenziemc@12: -- assume root is LoD mckenziemc@12: mckenziemc@12: -- check dependencies mckenziemc@12: local dependencies = {self.root:GetDependencies(addon)} mckenziemc@12: for i, depName in pairs(dependencies) do mckenziemc@12: local depTree = Tree:Get(depName) mckenziemc@12: depTree:PrepareForLoD() mckenziemc@12: mckenziemc@12: --[[ mckenziemc@12: if dep:CanLoD() then mckenziemc@12: -- don't load it now but make sure its dependencies are prepared mckenziemc@12: self:PrepareLoDTree(depName) mckenziemc@12: else mckenziemc@12: -- FIXME: if it's already loaded mckenziemc@12: -- force-load it now so we can load the parent on demand mckenziemc@12: self:ForceLoadTree(depName) mckenziemc@12: end mckenziemc@12: ]] mckenziemc@12: end mckenziemc@12: mckenziemc@12: -- prepare embeds, if they are available separately mckenziemc@12: local embeds = {self.root:GetEmbeds(addon)} -- FIXME: addon? mckenziemc@12: for i, embedName in pairs(embeds) do mckenziemc@12: local embedTree = Tree:Get(embedName) mckenziemc@12: embedTree:PrepareForLoD() mckenziemc@12: mckenziemc@12: --[[ mckenziemc@12: if embed then mckenziemc@12: if embed:CanLoD() then mckenziemc@12: -- don't load it now but make sure its dependencies are prepared mckenziemc@12: self:PrepareLoDTree(embedName) mckenziemc@12: else mckenziemc@12: -- FIXME: if it's already loaded mckenziemc@12: -- force-load it now so we can load the parent on demand mckenziemc@12: self:ForceLoadTree(depName) mckenziemc@12: end mckenziemc@12: end mckenziemc@12: ]] mckenziemc@12: end mckenziemc@12: end mckenziemc@12: mckenziemc@12: mckenziemc@8: -- load the root too, since it may actually be a leaf mckenziemc@10: function tree:ForceLoad() mckenziemc@8: -- load dependencies mckenziemc@10: local dependencies = {self.root:GetDependencies()} mckenziemc@8: for i, depName in pairs(dependencies) do mckenziemc@10: local depTree = Tree:Get(depName) mckenziemc@10: depTree:ForceLoad() mckenziemc@8: end mckenziemc@8: mckenziemc@8: -- load embeds, if they are available separately mckenziemc@10: local embeds = {self.root:GetEmbeds()} mckenziemc@8: for i, embedName in pairs(embeds) do mckenziemc@10: local embedTree = Tree:Get(embedName) mckenziemc@10: embedTree:ForceLoad() mckenziemc@8: end mckenziemc@8: mckenziemc@8: root:ForceLoad() mckenziemc@8: end mckenziemc@8: mckenziemc@8: mckenziemc@10: --[[ don't think i need this 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@10: ]] mckenziemc@8: