vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
Name: AceModuleCore-2.0
Revision: $Rev: 16530 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceModuleCore-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceModuleCore-2.0
Description: Mixin to provide a module system so that modules or plugins can
             use an addon as its core.
Dependencies: AceLibrary, AceOO-2.0, AceAddon-2.0, Compost-2.0 (optional)
]]

local MAJOR_VERSION = "AceModuleCore-2.0"
local MINOR_VERSION = "$Revision: 16530 $"

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end

local table_setn
do
        local version = GetBuildInfo()
        if string.find(version, "^2%.") then
                -- 2.0.0
                table_setn = function() end
        else
                table_setn = table.setn
        end
end

local new, del
do
        local list = setmetatable({}, {__mode = 'k'})
        function new()
                local t = next(list)
                if t then
                        list[t] = nil
                        return t
                else
                        return {}
                end
        end
        function del(t)
                for k in pairs(t) do
                        t[k] = nil
                end
                table_setn(t, 0)
                list[t] = true
                return nil
        end
end

local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local AceModuleCore = AceOO.Mixin {
                                                                        "NewModule",
                                                                        "HasModule",
                                                                        "GetModule",
                                                                        "IsModule",
                                                                        "IterateModules",
                                                                        "SetModuleMixins", 
                                                                        "SetModuleClass",
                                                                        "IsModuleActive",
                                                                        "ToggleModuleActive"
                                                                  }

local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")

local function getlibrary(lib)
        if type(lib) == "string" then
                return AceLibrary(lib)
        else
                return lib
        end
end

local tmp
function AceModuleCore:NewModule(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        if not self.modules then
                AceModuleCore:error("CreatePrototype() must be called before attempting to create a new module.", 2)
        end
        AceModuleCore:argCheck(name, 2, "string")
        if string.len(name) == 0 then
                AceModuleCore:error("Bad argument #2 to `NewModule`, string must not be empty")
        end
        if self.modules[name] then
                AceModuleCore:error("The module %q has already been registered", name)
        end

        if not tmp then
                tmp = {}
        end
        if a1 then table.insert(tmp, a1)
        if a2 then table.insert(tmp, a2)
        if a3 then table.insert(tmp, a3)
        if a4 then table.insert(tmp, a4)
        if a5 then table.insert(tmp, a5)
        if a6 then table.insert(tmp, a6)
        if a7 then table.insert(tmp, a7)
        if a8 then table.insert(tmp, a8)
        if a9 then table.insert(tmp, a9)
        if a10 then table.insert(tmp, a10)
        if a11 then table.insert(tmp, a11)
        if a12 then table.insert(tmp, a12)
        if a13 then table.insert(tmp, a13)
        if a14 then table.insert(tmp, a14)
        if a15 then table.insert(tmp, a15)
        if a16 then table.insert(tmp, a16)
        if a17 then table.insert(tmp, a17)
        if a18 then table.insert(tmp, a18)
        if a19 then table.insert(tmp, a19)
        if a20 then table.insert(tmp, a20)
        end end end end end end end end end end end end end end end end end end end end
        for k,v in ipairs(tmp) do
                tmp[k] = getlibrary(v)
        end

        if self.moduleMixins then
                for _,mixin in ipairs(self.moduleMixins) do
                        local exists = false
                        for _,v in ipairs(tmp) do
                                if mixin == v then
                                        exists = true
                                        break
                                end
                        end
                        if not exists then
                                table.insert(tmp, mixin)
                        end
                end
        end

        local module = AceOO.Classpool(self.moduleClass, unpack(tmp)):new(name)
        self.modules[name] = module
        module.name = name
        module.title = name

        AceModuleCore.totalModules[module] = self

        for k in pairs(tmp) do
                tmp[k] = nil
        end
        table_setn(tmp, 0)
        return module
end

function AceModuleCore:HasModule(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        if a1 then if not self.modules[a1] then return false end
        if a2 then if not self.modules[a2] then return false end
        if a3 then if not self.modules[a3] then return false end
        if a4 then if not self.modules[a4] then return false end
        if a5 then if not self.modules[a5] then return false end
        if a6 then if not self.modules[a6] then return false end
        if a7 then if not self.modules[a7] then return false end
        if a8 then if not self.modules[a8] then return false end
        if a9 then if not self.modules[a9] then return false end
        if a10 then if not self.modules[a10] then return false end
        if a11 then if not self.modules[a11] then return false end
        if a12 then if not self.modules[a12] then return false end
        if a13 then if not self.modules[a13] then return false end
        if a14 then if not self.modules[a14] then return false end
        if a15 then if not self.modules[a15] then return false end
        if a16 then if not self.modules[a16] then return false end
        if a17 then if not self.modules[a17] then return false end
        if a18 then if not self.modules[a18] then return false end
        if a19 then if not self.modules[a19] then return false end
        if a20 then if not self.modules[a20] then return false end
        end end end end end end end end end end end end end end end end end end end end

        return true
end

function AceModuleCore:GetModule(name)
        if not self.modules then
                AceModuleCore:error("Error initializing class.  Please report error.")
        end
        if not self.modules[name] then
                AceModuleCore:error("Cannot find module %q.", name)
        end
        return self.modules[name]
end

function AceModuleCore:IsModule(module)
        if self == AceModuleCore then
                return AceModuleCore.totalModules[module]
        else
                for k,v in pairs(self.modules) do
                        if v == module then
                                return true
                        end
                end
                return false
        end
end

function AceModuleCore:IterateModules()
        local t = new()
        for k in pairs(self.modules) do
                table.insert(t, k)
        end
        table.sort(t)
        local i = 0
        return function()
                i = i + 1
                local x = t[i]
                if x then
                        return x, self.modules[x]
                else
                        t = del(t)
                        return nil
                end
        end, nil, nil
end

function AceModuleCore:SetModuleMixins(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
        if self.moduleMixins then
                AceModuleCore:error('Cannot call "SetModuleMixins" twice')
        elseif not self.modules then
                AceModuleCore:error("Error initializing class.  Please report error.")
        elseif next(self.modules) then
                AceModuleCore:error('Cannot call "SetModuleMixins" after "NewModule" has been called.')
        end

        self.moduleMixins = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
        for k,v in ipairs(self.moduleMixins) do
                self.moduleMixins[k] = getlibrary(v)
        end
end

function AceModuleCore:SetModuleClass(class)
        class = getlibrary(class)
        AceModuleCore:assert(AceOO.inherits(class, AceOO.Class), "Bad argument #2 to `SetModuleClass' (Class expected)")
        if not self.modules then
                AceModuleCore:error("Error initializing class.  Please report error.")
        end
        if self.customModuleClass then
                AceModuleCore:error("Cannot call `SetModuleClass' twice.")
        end
        self.customModuleClass = true
        self.moduleClass = class
        self.modulePrototype = class.prototype
end

function AceModuleCore:ToggleModuleActive(module, state)
        AceModuleCore:argCheck(module, 2, "table", "string")
        AceModuleCore:argCheck(state, 3, "nil", "boolean")

        if type(module) == "string" then
                if not self:HasModule(module) then
                        AceModuleCore:error("Cannot find module %q", module)
                end
                module = self:GetModule(module)
        else
                if not self:IsModule(module) then
                        AceModuleCore:error("%q is not a module", module)
                end
        end

        local disable
        if state == nil then
                disable = self:IsModuleActive(module)
        else
                disable = not state
                if disable ~= self:IsModuleActive(module) then
                        return
                end
        end

        if type(module.ToggleActive) == "function" then
                return module:ToggleActive(not disable)
        elseif AceOO.inherits(self, "AceDB-2.0") then
                if not self.db or not self.db.raw then
                        AceModuleCore:error("Cannot toggle a module until `RegisterDB' has been called and `ADDON_LOADED' has been fireed.")
                end
                if type(self.db.raw.disabledModules) ~= "table" then
                        self.db.raw.disabledModules = Compost and Compost:Acquire() or {}
                end
                local _,profile = self:GetProfile()
                if type(self.db.raw.disabledModules[profile]) ~= "table" then
                        self.db.raw.disabledModules[profile] = Compost and Compost:Acquire() or {}
                end
                if type(self.db.raw.disabledModules[profile][module.name]) ~= "table" then
                        self.db.raw.disabledModules[profile][module.name] = disable or nil
                end
                if not disable then
                        if not next(self.db.raw.disabledModules[profile]) then
                                if Compost then
                                        Compost:Reclaim(self.db.raw.disabledModules[profile])
                                end
                                self.db.raw.disabledModules[profile] = nil
                        end
                        if not next(self.db.raw.disabledModules) then
                                if Compost then
                                        Compost:Reclaim(self.db.raw.disabledModules)
                                end
                                self.db.raw.disabledModules = nil
                        end
                end
        else
                if type(self.disabledModules) ~= "table" then
                        self.disabledModules = Compost and Compost:Acquire() or {}
                end
                self.disabledModules[module.name] = disable or nil
        end
        if AceOO.inherits(module, "AceAddon-2.0") then
                local AceAddon = AceLibrary("AceAddon-2.0")
                if not AceAddon.addonsStarted[module] then
                        return
                end
        end
        if not disable then
                local current = module.class
                while true do
                        if current == AceOO.Class then
                                break
                        end
                        if current.mixins then
                                for mixin in pairs(current.mixins) do
                                        if type(mixin.OnEmbedEnable) == "function" then
                                                mixin:OnEmbedEnable(module)
                                        end
                                end
                        end
                        current = current.super
                end
                if type(module.OnEnable) == "function" then
                        module:OnEnable()
                end
        else
                local current = module.class
                while true do
                        if current == AceOO.Class then
                                break
                        end
                        if current.mixins then
                                for mixin in pairs(current.mixins) do
                                        if type(mixin.OnEmbedDisable) == "function" then
                                                mixin:OnEmbedDisable(module)
                                        end
                                end
                        end
                        current = current.super
                end
                if type(module.OnDisable) == "function" then
                        module:OnDisable()
                end
        end
        return not disable
end

function AceModuleCore:IsModuleActive(module)
        AceModuleCore:argCheck(module, 2, "table", "string")
        
        if AceModuleCore == self then
                self:argCheck(module, 2, "table")
                
                local core = AceModuleCore.totalModules[module]
                if not core then
                        self:error("Bad argument #2 to `IsModuleActive'. Not a module")
                end
                return core:IsModuleActive(module)
        end
        
        if type(module) == "string" then
                if not self:HasModule(module) then
                        AceModuleCore:error("Cannot find module %q", module)
                end
                module = self:GetModule(module)
        else
                if not self:IsModule(module) then
                        AceModuleCore:error("%q is not a module", module)
                end
        end

        if type(module.IsActive) == "function" then
                return module:IsActive()
        elseif AceOO.inherits(self, "AceDB-2.0") then
                local _,profile = self:GetProfile()
                return not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[profile] or not self.db.raw.disabledModules[profile][module.name]
        else
                return not self.disabledModules or not self.disabledModules[module.name]
        end
end

function AceModuleCore:OnInstanceInit(target)
        if target.modules then
                AceModuleCore:error("OnInstanceInit cannot be called twice")
        end
        target.modules = Compost and Compost:Acquire() or {}

        target.moduleClass = AceOO.Class("AceAddon-2.0")
        target.modulePrototype = target.moduleClass.prototype
end

AceModuleCore.OnManualEmbed = AceModuleCore.OnInstanceInit

function AceModuleCore.OnEmbedProfileDisable(AceModuleCore, self, newProfile)
        if not AceOO.inherits(self, "AceDB-2.0") then
                return
        end
        local _,currentProfile = self:GetProfile()
        for k, module in pairs(self.modules) do
                if type(module.IsActive) == "function" or type(module.ToggleActive) == "function" then
                        -- continue
                else
                        local currentActive =  not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[currentProfile] or not self.db.raw.disabledModules[currentProfile][module.name]
                        local newActive =  not self.db or not self.db.raw or not self.db.raw.disabledModules or not self.db.raw.disabledModules[newProfile] or not self.db.raw.disabledModules[newProfile][module.name]
                        if currentActive ~= newActive then
                                self:ToggleModuleActive(module)
                                if not self.db.raw.disabledModules then
                                        self.db.raw.disabledModules = {}
                                end
                                if not self.db.raw.disabledModules[currentProfile] then
                                        self.db.raw.disabledModules[currentProfile] = {}
                                end
                                self.db.raw.disabledModules[currentProfile][module.name] = not currentActive or nil
                        end
                end
        end
end

local function activate(self, oldLib, oldDeactivate)
        AceModuleCore = self

        self.super.activate(self, oldLib, oldDeactivate)

        if oldLib then
                self.totalModules = oldLib.totalModules
        end
        if not self.totalModules then
                self.totalModules = {}
        end
end

local function external(self, major, instance)
        if major == "Compost-2.0" then
                Compost = instance
        end
end

AceLibrary:Register(AceModuleCore, MAJOR_VERSION, MINOR_VERSION, activate)
AceModuleCore = AceLibrary(MAJOR_VERSION)