flickerstreak@1: --[[ flickerstreak@1: Name: AceOO-2.0 flickerstreak@22: Revision: $Rev: 38641 $ flickerstreak@1: Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) flickerstreak@1: Inspired By: Ace 1.x by Turan (turan@gryphon.com) flickerstreak@1: Website: http://www.wowace.com/ flickerstreak@1: Documentation: http://www.wowace.com/index.php/AceOO-2.0 flickerstreak@1: SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0 flickerstreak@1: Description: Library to provide an object-orientation framework. flickerstreak@1: Dependencies: AceLibrary flickerstreak@22: License: MIT flickerstreak@1: ]] flickerstreak@1: flickerstreak@1: local MAJOR_VERSION = "AceOO-2.0" flickerstreak@22: local MINOR_VERSION = "$Revision: 38641 $" flickerstreak@1: flickerstreak@1: -- This ensures the code is only executed if the libary doesn't already exist, or is a newer version flickerstreak@1: if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end flickerstreak@1: if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end flickerstreak@1: flickerstreak@1: local AceOO = { flickerstreak@1: error = AceLibrary.error, flickerstreak@1: argCheck = AceLibrary.argCheck flickerstreak@1: } flickerstreak@1: flickerstreak@1: -- @function getuid flickerstreak@1: -- @brief Obtain a unique string identifier for the object in question. flickerstreak@1: -- @param t The object to obtain the uid for. flickerstreak@1: -- @return The uid string. flickerstreak@1: local function getuid(t) flickerstreak@1: local mt = getmetatable(t) flickerstreak@1: setmetatable(t, nil) flickerstreak@1: local str = tostring(t) flickerstreak@1: setmetatable(t, mt) flickerstreak@22: local cap = str:match("[^:]*: 0x(.*)$") or str:match("[^:]*: (.*)$") flickerstreak@22: if cap then flickerstreak@22: return ("0"):rep(8 - #cap) .. cap flickerstreak@22: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function getlibrary(o) flickerstreak@1: if type(o) == "table" then flickerstreak@1: return o flickerstreak@1: elseif type(o) == "string" then flickerstreak@1: if not AceLibrary:HasInstance(o) then flickerstreak@1: AceOO:error("Library %q does not exist.", o) flickerstreak@1: end flickerstreak@1: return AceLibrary(o) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@22: local function deeprawget(self, k) flickerstreak@22: while true do flickerstreak@22: local v = rawget(self, k) flickerstreak@22: if v ~= nil then flickerstreak@22: return v flickerstreak@22: end flickerstreak@22: local mt = getmetatable(self) flickerstreak@22: if not mt or type(mt.__index) ~= "table" then flickerstreak@22: return nil flickerstreak@22: end flickerstreak@22: self = mt.__index flickerstreak@22: end flickerstreak@22: end flickerstreak@22: flickerstreak@1: -- @function Factory flickerstreak@1: -- @brief Construct a factory for the creation of objects. flickerstreak@1: -- @param obj The object whose init method will be called on the new factory flickerstreak@1: -- object. flickerstreak@1: -- @param newobj The object whose init method will be called on the new flickerstreak@1: -- objects that the Factory creates, to initialize them. flickerstreak@1: -- @param (...) Arguments which will be passed to obj.init() in addition flickerstreak@1: -- to the Factory object. flickerstreak@1: -- @return The new factory which creates a newobj when its new method is called, flickerstreak@1: -- or when it is called directly (__call metamethod). flickerstreak@1: local Factory flickerstreak@1: do flickerstreak@22: local function getlibraries(...) flickerstreak@22: if select('#', ...) == 0 then flickerstreak@22: return flickerstreak@22: end flickerstreak@22: return getlibrary((select(1, ...))), getlibraries(select(2, ...)) flickerstreak@22: end flickerstreak@1: local arg = {} flickerstreak@1: local function new(obj, ...) flickerstreak@1: local t = {} flickerstreak@1: local uid = getuid(t) flickerstreak@22: obj:init(t, getlibraries(...)) flickerstreak@1: t.uid = uid flickerstreak@1: return t flickerstreak@1: end flickerstreak@1: flickerstreak@1: local function createnew(self, ...) flickerstreak@1: local o = self.prototype flickerstreak@22: local x = new(o, getlibraries(...)) flickerstreak@1: return x flickerstreak@1: end flickerstreak@1: flickerstreak@1: function Factory(obj, newobj, ...) flickerstreak@1: local t = new(obj, ...) flickerstreak@1: t.prototype = newobj flickerstreak@1: t.new = createnew flickerstreak@1: getmetatable(t).__call = t.new flickerstreak@1: return t flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: local function objtostring(self) flickerstreak@1: if self.ToString then flickerstreak@1: return self:ToString() flickerstreak@1: elseif self.GetLibraryVersion then flickerstreak@1: return (self:GetLibraryVersion()) flickerstreak@1: elseif self.super then flickerstreak@1: local s = "Sub-" .. tostring(self.super) flickerstreak@1: local first = true flickerstreak@1: if self.interfaces then flickerstreak@1: for interface in pairs(self.interfaces) do flickerstreak@1: if first then flickerstreak@1: s = s .. "(" .. tostring(interface) flickerstreak@1: first = false flickerstreak@1: else flickerstreak@1: s = s .. ", " .. tostring(interface) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if self.mixins then flickerstreak@1: for mixin in pairs(self.mixins) do flickerstreak@1: if first then flickerstreak@1: s = s .. tostring(mixin) flickerstreak@1: first = false flickerstreak@1: else flickerstreak@1: s = s .. ", " .. tostring(mixin) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if first then flickerstreak@1: if self.uid then flickerstreak@1: return s .. ":" .. self.uid flickerstreak@1: else flickerstreak@1: return s flickerstreak@1: end flickerstreak@1: else flickerstreak@1: return s .. ")" flickerstreak@1: end flickerstreak@1: else flickerstreak@1: return self.uid and 'Subclass:' .. self.uid or 'Subclass' flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- @table Object flickerstreak@1: -- @brief Base of all objects, including Class. flickerstreak@1: -- flickerstreak@1: -- @method init flickerstreak@1: -- @brief Initialize a new object. flickerstreak@1: -- @param newobject The object to initialize flickerstreak@1: -- @param class The class to make newobject inherit from flickerstreak@1: local Object flickerstreak@1: do flickerstreak@1: Object = {} flickerstreak@1: function Object:init(newobject, class) flickerstreak@1: local parent = class or self flickerstreak@1: if not rawget(newobject, 'uid') then flickerstreak@1: newobject.uid = getuid(newobject) flickerstreak@1: end flickerstreak@1: local mt = { flickerstreak@1: __index = parent, flickerstreak@1: __tostring = objtostring, flickerstreak@1: } flickerstreak@1: setmetatable(newobject, mt) flickerstreak@1: end flickerstreak@1: Object.uid = getuid(Object) flickerstreak@1: setmetatable(Object, { __tostring = function() return 'Object' end }) flickerstreak@1: end flickerstreak@1: flickerstreak@1: local Interface flickerstreak@1: flickerstreak@1: local function validateInterface(object, interface) flickerstreak@1: if not object.class and object.prototype then flickerstreak@1: object = object.prototype flickerstreak@1: end flickerstreak@1: for k,v in pairs(interface.interface) do flickerstreak@1: if tostring(type(object[k])) ~= v then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if interface.superinterfaces then flickerstreak@1: for superinterface in pairs(interface.superinterfaces) do flickerstreak@1: if not validateInterface(object, superinterface) then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if type(object.class) == "table" and rawequal(object.class.prototype, object) then flickerstreak@1: if not object.class.interfaces then flickerstreak@1: rawset(object.class, 'interfaces', {}) flickerstreak@1: end flickerstreak@1: object.class.interfaces[interface] = true flickerstreak@1: elseif type(object.class) == "table" and type(object.class.prototype) == "table" then flickerstreak@1: validateInterface(object.class.prototype, interface) flickerstreak@1: -- check if class is proper, thus preventing future checks. flickerstreak@1: end flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- @function inherits flickerstreak@1: -- @brief Return whether an Object or Class inherits from a given flickerstreak@1: -- parent. flickerstreak@1: -- @param object Object or Class to check flickerstreak@1: -- @param parent Parent to test inheritance from flickerstreak@1: -- @return whether an Object or Class inherits from a given flickerstreak@1: -- parent. flickerstreak@1: local function inherits(object, parent) flickerstreak@1: object = getlibrary(object) flickerstreak@1: if type(parent) == "string" then flickerstreak@1: if not AceLibrary:HasInstance(parent) then flickerstreak@1: return false flickerstreak@1: else flickerstreak@1: parent = AceLibrary(parent) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: AceOO:argCheck(parent, 2, "table") flickerstreak@1: if type(object) ~= "table" then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: local current flickerstreak@22: local class = deeprawget(object, 'class') flickerstreak@22: if class then flickerstreak@22: current = class flickerstreak@1: else flickerstreak@1: current = object flickerstreak@1: end flickerstreak@1: if type(current) ~= "table" then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: if rawequal(current, parent) then flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: if parent.class then flickerstreak@1: while true do flickerstreak@1: if rawequal(current, Object) then flickerstreak@1: break flickerstreak@1: end flickerstreak@1: if current.mixins then flickerstreak@1: for mixin in pairs(current.mixins) do flickerstreak@1: if rawequal(mixin, parent) then flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if current.interfaces then flickerstreak@1: for interface in pairs(current.interfaces) do flickerstreak@1: if rawequal(interface, parent) then flickerstreak@1: return true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@22: current = deeprawget(current, 'super') flickerstreak@1: if type(current) ~= "table" then flickerstreak@1: break flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local isInterface = false flickerstreak@1: local curr = parent.class flickerstreak@1: while true do flickerstreak@1: if rawequal(curr, Object) then flickerstreak@1: break flickerstreak@1: elseif rawequal(curr, Interface) then flickerstreak@1: isInterface = true flickerstreak@1: break flickerstreak@1: end flickerstreak@22: curr = deeprawget(curr, 'super') flickerstreak@1: if type(curr) ~= "table" then flickerstreak@1: break flickerstreak@1: end flickerstreak@1: end flickerstreak@1: return isInterface and validateInterface(object, parent) flickerstreak@1: else flickerstreak@1: while true do flickerstreak@1: if rawequal(current, parent) then flickerstreak@1: return true flickerstreak@1: elseif rawequal(current, Object) then flickerstreak@1: return false flickerstreak@1: end flickerstreak@22: current = deeprawget(current, 'super') flickerstreak@1: if type(current) ~= "table" then flickerstreak@1: return false flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- @table Class flickerstreak@1: -- @brief An object factory which sets up inheritence and supports flickerstreak@1: -- 'mixins'. flickerstreak@1: -- flickerstreak@1: -- @metamethod Class call flickerstreak@1: -- @brief Call ClassFactory:new() to create a new class. flickerstreak@1: -- flickerstreak@1: -- @method Class new flickerstreak@1: -- @brief Construct a new object. flickerstreak@1: -- @param (...) Arguments to pass to the object init function. flickerstreak@1: -- @return The new object. flickerstreak@1: -- flickerstreak@1: -- @method Class init flickerstreak@1: -- @brief Initialize a new class. flickerstreak@1: -- @param parent Superclass. flickerstreak@1: -- @param (...) Mixins. flickerstreak@1: -- flickerstreak@1: -- @method Class ToString flickerstreak@1: -- @return A string representing the object, in this case 'Class'. flickerstreak@1: local initStatus flickerstreak@1: local Class flickerstreak@1: local Mixin flickerstreak@1: local autoEmbed = false flickerstreak@1: local function traverseInterfaces(bit, total) flickerstreak@1: if bit.superinterfaces then flickerstreak@1: for interface in pairs(bit.superinterfaces) do flickerstreak@1: if not total[interface] then flickerstreak@1: total[interface] = true flickerstreak@1: traverseInterfaces(interface, total) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local class_new flickerstreak@1: do flickerstreak@1: Class = Factory(Object, setmetatable({}, {__index = Object}), Object) flickerstreak@1: Class.super = Object flickerstreak@1: flickerstreak@1: local function protostring(t) flickerstreak@1: return '<' .. tostring(t.class) .. ' prototype>' flickerstreak@1: end flickerstreak@1: local function classobjectstring(t) flickerstreak@1: if t.ToString then flickerstreak@1: return t:ToString() flickerstreak@1: elseif t.GetLibraryVersion then flickerstreak@1: return (t:GetLibraryVersion()) flickerstreak@1: else flickerstreak@1: return '<' .. tostring(t.class) .. ' instance>' flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectequal(self, other) flickerstreak@1: if type(self) == "table" and self.Equals then flickerstreak@1: return self:Equals(other) flickerstreak@1: elseif type(other) == "table" and other.Equals then flickerstreak@1: return other:Equals(self) flickerstreak@1: elseif type(self) == "table" and self.CompareTo then flickerstreak@1: return self:CompareTo(other) == 0 flickerstreak@1: elseif type(other) == "table" and other.CompareTo then flickerstreak@1: return other:CompareTo(self) == 0 flickerstreak@1: else flickerstreak@1: return rawequal(self, other) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectlessthan(self, other) flickerstreak@1: if type(self) == "table" and self.IsLessThan then flickerstreak@1: return self:IsLessThan(other) flickerstreak@1: elseif type(other) == "table" and other.IsLessThanOrEqualTo then flickerstreak@1: return not other:IsLessThanOrEqualTo(self) flickerstreak@1: elseif type(self) == "table" and self.CompareTo then flickerstreak@1: return self:CompareTo(other) < 0 flickerstreak@1: elseif type(other) == "table" and other.CompareTo then flickerstreak@1: return other:CompareTo(self) > 0 flickerstreak@1: elseif type(other) == "table" and other.IsLessThan and other.Equals then flickerstreak@1: return other:Equals(self) or other:IsLessThan(self) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot compare two objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectlessthanequal(self, other) flickerstreak@1: if type(self) == "table" and self.IsLessThanOrEqualTo then flickerstreak@1: return self:IsLessThanOrEqualTo(other) flickerstreak@1: elseif type(other) == "table" and other.IsLessThan then flickerstreak@1: return not other:IsLessThan(self) flickerstreak@1: elseif type(self) == "table" and self.CompareTo then flickerstreak@1: return self:CompareTo(other) <= 0 flickerstreak@1: elseif type(other) == "table" and other.CompareTo then flickerstreak@1: return other:CompareTo(self) >= 0 flickerstreak@1: elseif type(self) == "table" and self.IsLessThan and self.Equals then flickerstreak@1: return self:Equals(other) or self:IsLessThan(other) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot compare two incompatible objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectadd(self, other) flickerstreak@1: if type(self) == "table" and self.Add then flickerstreak@1: return self:Add(other) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot add two incompatible objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectsub(self, other) flickerstreak@1: if type(self) == "table" and self.Subtract then flickerstreak@1: return self:Subtract(other) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot subtract two incompatible objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectunm(self, other) flickerstreak@1: if type(self) == "table" and self.UnaryNegation then flickerstreak@1: return self:UnaryNegation(other) flickerstreak@1: else flickerstreak@1: AceOO:error("attempt to negate an incompatible object") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectmul(self, other) flickerstreak@1: if type(self) == "table" and self.Multiply then flickerstreak@1: return self:Multiply(other) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot multiply two incompatible objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectdiv(self, other) flickerstreak@1: if type(self) == "table" and self.Divide then flickerstreak@1: return self:Divide(other) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot divide two incompatible objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectpow(self, other) flickerstreak@1: if type(self) == "table" and self.Exponent then flickerstreak@1: return self:Exponent(other) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot exponentiate two incompatible objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function classobjectconcat(self, other) flickerstreak@1: if type(self) == "table" and self.Concatenate then flickerstreak@1: return self:Concatenate(other) flickerstreak@1: else flickerstreak@1: AceOO:error("cannot concatenate two incompatible objects") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: function class_new(self, ...) flickerstreak@1: if self.virtual then flickerstreak@1: AceOO:error("Cannot instantiate a virtual class.") flickerstreak@1: end flickerstreak@1: flickerstreak@1: local o = self.prototype flickerstreak@1: local newobj = {} flickerstreak@1: if o.class and o.class.instancemeta then flickerstreak@1: setmetatable(newobj, o.class.instancemeta) flickerstreak@1: else flickerstreak@1: Object:init(newobj, o) flickerstreak@1: end flickerstreak@1: flickerstreak@1: if self.interfaces and not self.interfacesVerified then flickerstreak@1: -- Verify the interfaces flickerstreak@1: flickerstreak@1: for interface in pairs(self.interfaces) do flickerstreak@1: for field,kind in pairs(interface.interface) do flickerstreak@1: if tostring(type(newobj[field])) ~= kind then flickerstreak@1: AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field]))) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: self.interfacesVerified = true flickerstreak@1: end flickerstreak@1: local tmp = initStatus flickerstreak@1: initStatus = newobj flickerstreak@1: newobj:init(...) flickerstreak@1: if initStatus then flickerstreak@1: initStatus = tmp flickerstreak@1: AceOO:error("Initialization not completed, be sure to call the superclass's init method.") flickerstreak@1: return flickerstreak@1: end flickerstreak@1: initStatus = tmp flickerstreak@1: return newobj flickerstreak@1: end flickerstreak@1: local classmeta = { flickerstreak@1: __tostring = objtostring, flickerstreak@1: __call = function(self, ...) flickerstreak@1: return self:new(...) flickerstreak@1: end, flickerstreak@1: } flickerstreak@1: function Class:init(newclass, parent, ...) flickerstreak@1: parent = parent or self flickerstreak@1: flickerstreak@1: local total flickerstreak@1: flickerstreak@1: if parent.class then flickerstreak@1: total = { parent, ... } flickerstreak@1: parent = self flickerstreak@1: else flickerstreak@1: total = { ... } flickerstreak@1: end flickerstreak@1: if not inherits(parent, Class) then flickerstreak@1: AceOO:error("Classes must inherit from a proper class") flickerstreak@1: end flickerstreak@1: if parent.sealed then flickerstreak@1: AceOO:error("Cannot inherit from a sealed class") flickerstreak@1: end flickerstreak@1: for i,v in ipairs(total) do flickerstreak@1: if inherits(v, Mixin) and v.class then flickerstreak@1: if v.__deprecated then flickerstreak@1: AceOO:error(v.__deprecated) flickerstreak@1: end flickerstreak@1: if not newclass.mixins then flickerstreak@1: newclass.mixins = {} flickerstreak@1: end flickerstreak@1: if newclass.mixins[v] then flickerstreak@1: AceOO:error("Cannot explicitly inherit from the same mixin twice") flickerstreak@1: end flickerstreak@1: newclass.mixins[v] = true flickerstreak@1: elseif inherits(v, Interface) and v.class then flickerstreak@1: if not newclass.interfaces then flickerstreak@1: newclass.interfaces = {} flickerstreak@1: end flickerstreak@1: if newclass.interfaces[v] then flickerstreak@1: AceOO:error("Cannot explicitly inherit from the same interface twice") flickerstreak@1: end flickerstreak@1: newclass.interfaces[v] = true flickerstreak@1: else flickerstreak@1: AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if parent.interfaces then flickerstreak@1: if not newclass.interfaces then flickerstreak@1: newclass.interfaces = {} flickerstreak@1: end flickerstreak@1: for interface in pairs(parent.interfaces) do flickerstreak@1: newclass.interfaces[interface] = true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: for k in pairs(total) do flickerstreak@1: total[k] = nil flickerstreak@1: end flickerstreak@1: flickerstreak@1: newclass.super = parent flickerstreak@1: flickerstreak@1: newclass.prototype = setmetatable(total, { flickerstreak@1: __index = parent.prototype, flickerstreak@1: __tostring = protostring, flickerstreak@1: }) flickerstreak@1: total = nil flickerstreak@1: flickerstreak@1: newclass.instancemeta = { flickerstreak@1: __index = newclass.prototype, flickerstreak@1: __tostring = classobjectstring, flickerstreak@1: __eq = classobjectequal, flickerstreak@1: __lt = classobjectlessthan, flickerstreak@1: __le = classobjectlessthanequal, flickerstreak@1: __add = classobjectadd, flickerstreak@1: __sub = classobjectsub, flickerstreak@1: __unm = classobjectunm, flickerstreak@1: __mul = classobjectmul, flickerstreak@1: __div = classobjectdiv, flickerstreak@1: __pow = classobjectpow, flickerstreak@1: __concat = classobjectconcat, flickerstreak@1: } flickerstreak@1: flickerstreak@1: setmetatable(newclass, classmeta) flickerstreak@1: flickerstreak@1: newclass.new = class_new flickerstreak@1: flickerstreak@1: if newclass.mixins then flickerstreak@1: -- Fold in the mixins flickerstreak@1: local err, msg flickerstreak@1: for mixin in pairs(newclass.mixins) do flickerstreak@1: local ret flickerstreak@1: autoEmbed = true flickerstreak@1: ret, msg = pcall(mixin.embed, mixin, newclass.prototype) flickerstreak@1: autoEmbed = false flickerstreak@1: if not ret then flickerstreak@1: err = true flickerstreak@1: break flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: if err then flickerstreak@1: local pt = newclass.prototype flickerstreak@1: for k,v in pairs(pt) do flickerstreak@1: pt[k] = nil flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- method conflict flickerstreak@1: AceOO:error(msg) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: newclass.prototype.class = newclass flickerstreak@1: flickerstreak@1: if newclass.interfaces then flickerstreak@1: for interface in pairs(newclass.interfaces) do flickerstreak@1: traverseInterfaces(interface, newclass.interfaces) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if newclass.mixins then flickerstreak@1: for mixin in pairs(newclass.mixins) do flickerstreak@1: if mixin.interfaces then flickerstreak@1: if not newclass.interfaces then flickerstreak@1: newclass.interfaces = {} flickerstreak@1: end flickerstreak@1: for interface in pairs(mixin.interfaces) do flickerstreak@1: newclass.interfaces[interface] = true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: function Class:ToString() flickerstreak@1: if type(self.GetLibraryVersion) == "function" then flickerstreak@1: return (self:GetLibraryVersion()) flickerstreak@1: else flickerstreak@1: return "Class" flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: local tmp flickerstreak@1: function Class.prototype:init() flickerstreak@1: if rawequal(self, initStatus) then flickerstreak@1: initStatus = nil flickerstreak@1: else flickerstreak@1: AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2) flickerstreak@1: end flickerstreak@1: self.uid = getuid(self) flickerstreak@1: local current = self.class flickerstreak@1: while true do flickerstreak@1: if current == Class then flickerstreak@1: break flickerstreak@1: end flickerstreak@1: if current.mixins then flickerstreak@1: for mixin in pairs(current.mixins) do flickerstreak@1: if type(mixin.OnInstanceInit) == "function" then flickerstreak@1: mixin:OnInstanceInit(self) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: current = current.super flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: -- @object ClassFactory flickerstreak@1: -- @brief A factory for creating classes. Rarely used directly. flickerstreak@1: local ClassFactory = Factory(Object, Class, Object) flickerstreak@1: flickerstreak@1: function Class:new(...) flickerstreak@1: local x = ClassFactory:new(...) flickerstreak@1: if AceOO.classes then flickerstreak@1: AceOO.classes[x] = true flickerstreak@1: end flickerstreak@1: return x flickerstreak@1: end flickerstreak@1: getmetatable(Class).__call = Class.new flickerstreak@1: flickerstreak@1: -- @class Mixin flickerstreak@1: -- @brief A class to create mixin objects, which contain methods that get flickerstreak@1: -- "mixed in" to class prototypes. flickerstreak@1: -- flickerstreak@1: -- @object Mixin prototype flickerstreak@1: -- @brief The prototype that mixin objects inherit their methods from. flickerstreak@1: -- flickerstreak@1: -- @method Mixin prototype embed flickerstreak@1: -- @brief Mix in the methods of our object which are listed in our interface flickerstreak@1: -- to the supplied target table. flickerstreak@1: -- flickerstreak@1: -- @method Mixin prototype init flickerstreak@1: -- @brief Initialize the mixin object. flickerstreak@1: -- @param newobj The new object we're initializing. flickerstreak@1: -- @param interface The interface we implement (the list of methods our flickerstreak@1: -- prototype provides which should be mixed into the target flickerstreak@1: -- table by embed). flickerstreak@1: do flickerstreak@1: Mixin = Class() flickerstreak@1: function Mixin:ToString() flickerstreak@1: if self.GetLibraryVersion then flickerstreak@1: return (self:GetLibraryVersion()) flickerstreak@1: else flickerstreak@1: return 'Mixin' flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local function _Embed(state, field, target) flickerstreak@1: field = next(state.export, field) flickerstreak@1: if field == nil then flickerstreak@1: return flickerstreak@1: end flickerstreak@1: flickerstreak@1: if rawget(target, field) or (target[field] and target[field] ~= state[field]) then flickerstreak@1: AceOO:error("Method conflict in attempt to mixin. Field %q", field) flickerstreak@1: end flickerstreak@1: flickerstreak@1: target[field] = state[field] flickerstreak@1: flickerstreak@1: local ret,msg = pcall(_Embed, state, field, target) flickerstreak@1: if not ret then flickerstreak@1: -- Mix in the next method according to the defined interface. If that flickerstreak@1: -- fails due to a conflict, re-raise to back out the previous mixed flickerstreak@1: -- methods. flickerstreak@1: flickerstreak@1: target[field] = nil flickerstreak@1: AceOO:error(msg) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: function Mixin.prototype:embed(target) flickerstreak@1: if self.__deprecated then flickerstreak@1: AceOO:error(self.__deprecated) flickerstreak@1: end flickerstreak@1: local mt = getmetatable(target) flickerstreak@1: setmetatable(target, nil) flickerstreak@1: local err, msg = pcall(_Embed, self, nil, target) flickerstreak@1: if not err then flickerstreak@1: setmetatable(target, mt) flickerstreak@1: AceOO:error(msg) flickerstreak@1: return flickerstreak@1: end flickerstreak@1: if type(self.embedList) == "table" then flickerstreak@1: self.embedList[target] = true flickerstreak@1: end flickerstreak@1: if type(target.class) ~= "table" then flickerstreak@1: target[self] = true flickerstreak@1: end flickerstreak@1: if not autoEmbed and type(self.OnManualEmbed) == "function" then flickerstreak@1: self:OnManualEmbed(target) flickerstreak@1: end flickerstreak@1: setmetatable(target, mt) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function Mixin.prototype:activate(oldLib, oldDeactivate) flickerstreak@1: if oldLib and oldLib.embedList then flickerstreak@1: for target in pairs(oldLib.embedList) do flickerstreak@1: local mt = getmetatable(target) flickerstreak@1: setmetatable(target, nil) flickerstreak@1: for field in pairs(oldLib.export) do flickerstreak@1: target[field] = nil flickerstreak@1: end flickerstreak@1: setmetatable(target, mt) flickerstreak@1: end flickerstreak@1: self.embedList = oldLib.embedList flickerstreak@1: for target in pairs(self.embedList) do flickerstreak@1: self:embed(target) flickerstreak@1: end flickerstreak@1: else flickerstreak@1: self.embedList = setmetatable({}, {__mode="k"}) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function Mixin.prototype:init(export, ...) flickerstreak@1: AceOO:argCheck(export, 2, "table") flickerstreak@1: for k,v in pairs(export) do flickerstreak@1: if type(k) ~= "number" then flickerstreak@1: AceOO:error("All keys to argument #2 must be numbers.") flickerstreak@1: elseif type(v) ~= "string" then flickerstreak@1: AceOO:error("All values to argument #2 must be strings.") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local num = #export flickerstreak@1: for i = 1, num do flickerstreak@1: local v = export[i] flickerstreak@1: export[i] = nil flickerstreak@1: export[v] = true flickerstreak@1: end flickerstreak@1: flickerstreak@1: local interfaces flickerstreak@1: if select('#', ...) >= 1 then flickerstreak@1: interfaces = { ... } flickerstreak@1: for i,v in ipairs(interfaces) do flickerstreak@1: v = getlibrary(v) flickerstreak@1: interfaces[i] = v flickerstreak@1: if not v.class or not inherits(v, Interface) then flickerstreak@1: AceOO:error("Mixins can inherit only from interfaces") flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local num = #interfaces flickerstreak@1: for i = 1, num do flickerstreak@1: local v = interfaces[i] flickerstreak@1: interfaces[i] = nil flickerstreak@1: interfaces[v] = true flickerstreak@1: end flickerstreak@1: for interface in pairs(interfaces) do flickerstreak@1: traverseInterfaces(interface, interfaces) flickerstreak@1: end flickerstreak@1: for interface in pairs(interfaces) do flickerstreak@1: for field,kind in pairs(interface.interface) do flickerstreak@1: if kind ~= "nil" then flickerstreak@1: local good = false flickerstreak@1: for bit in pairs(export) do flickerstreak@1: if bit == field then flickerstreak@1: good = true flickerstreak@1: break flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if not good then flickerstreak@1: AceOO:error("Mixin does not fully accommodate field %q", field) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: self.super = Mixin.prototype flickerstreak@1: Mixin.super.prototype.init(self) flickerstreak@1: self.export = export flickerstreak@1: self.interfaces = interfaces flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- @class Interface flickerstreak@1: -- @brief A class to create interfaces, which contain contracts that classes flickerstreak@1: -- which inherit from this must comply with. flickerstreak@1: -- flickerstreak@1: -- @object Interface prototype flickerstreak@1: -- @brief The prototype that interface objects must adhere to. flickerstreak@1: -- flickerstreak@1: -- @method Interface prototype init flickerstreak@1: -- @brief Initialize the mixin object. flickerstreak@1: -- @param interface The interface we contract (the hash of fields forced). flickerstreak@1: -- @param (...) Superinterfaces flickerstreak@1: do flickerstreak@1: Interface = Class() flickerstreak@1: function Interface:ToString() flickerstreak@1: if self.GetLibraryVersion then flickerstreak@1: return (self:GetLibraryVersion()) flickerstreak@1: else flickerstreak@1: return 'Instance' flickerstreak@1: end flickerstreak@1: end flickerstreak@1: function Interface.prototype:init(interface, ...) flickerstreak@1: Interface.super.prototype.init(self) flickerstreak@1: AceOO:argCheck(interface, 2, "table") flickerstreak@1: for k,v in pairs(interface) do flickerstreak@1: if type(k) ~= "string" then flickerstreak@1: AceOO:error("All keys to argument #2 must be numbers.") flickerstreak@1: elseif type(v) ~= "string" then flickerstreak@1: AceOO:error("All values to argument #2 must be strings.") flickerstreak@1: elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then flickerstreak@1: AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".') flickerstreak@1: end flickerstreak@1: end flickerstreak@1: if select('#', ...) >= 1 then flickerstreak@1: self.superinterfaces = { ... } flickerstreak@1: for i,v in ipairs(self.superinterfaces) do flickerstreak@1: v = getlibrary(v) flickerstreak@1: self.superinterfaces[i] = v flickerstreak@1: if not inherits(v, Interface) or not v.class then flickerstreak@1: AceOO:error('Cannot provide a non-Interface to inherit from') flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local num = #self.superinterfaces flickerstreak@1: for i = 1, num do flickerstreak@1: local v = self.superinterfaces[i] flickerstreak@1: self.superinterfaces[i] = nil flickerstreak@1: self.superinterfaces[v] = true flickerstreak@1: end flickerstreak@1: end flickerstreak@1: self.interface = interface flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: -- @function Classpool flickerstreak@1: -- @brief Obtain a read only class from our pool of classes, indexed by the flickerstreak@1: -- superclass and mixins. flickerstreak@1: -- @param sc The superclass of the class we want. flickerstreak@1: -- @param (m1..m20) Mixins of the class we want's objects. flickerstreak@1: -- @return A read only class from the class pool. flickerstreak@1: local Classpool flickerstreak@1: do flickerstreak@1: local pool = setmetatable({}, {__mode = 'v'}) flickerstreak@1: local function newindex(k, v) flickerstreak@1: AceOO:error('Attempt to modify a read-only class.') flickerstreak@1: end flickerstreak@1: local function protonewindex(k, v) flickerstreak@1: AceOO:error('Attempt to modify a read-only class prototype.') flickerstreak@1: end flickerstreak@1: local function ts(bit) flickerstreak@1: if type(bit) ~= "table" then flickerstreak@1: return tostring(bit) flickerstreak@1: elseif getmetatable(bit) and bit.__tostring then flickerstreak@1: return tostring(bit) flickerstreak@1: elseif type(bit.GetLibraryVersion) == "function" then flickerstreak@1: return bit:GetLibraryVersion() flickerstreak@1: else flickerstreak@1: return tostring(bit) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: local t = {} flickerstreak@1: local function getcomplexuid(sc, ...) flickerstreak@1: if sc then flickerstreak@1: if sc.uid then flickerstreak@1: table.insert(t, sc.uid) flickerstreak@1: else flickerstreak@1: AceOO:error("%s is not an appropriate class/mixin", ts(sc)) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: for i = 1, select('#', ...) do flickerstreak@1: local m = select(i, ...) flickerstreak@1: if m.uid then flickerstreak@1: table.insert(t, m.uid) flickerstreak@1: else flickerstreak@1: AceOO:error("%s is not an appropriate mixin", ts(m)) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: table.sort(t) flickerstreak@1: local uid = table.concat(t, '') flickerstreak@1: local num = #t flickerstreak@1: for i = 1, num do flickerstreak@1: t[i] = nil flickerstreak@1: end flickerstreak@1: return uid flickerstreak@1: end flickerstreak@1: local classmeta flickerstreak@1: local arg = {} flickerstreak@1: function Classpool(superclass, ...) flickerstreak@1: local l = getlibrary flickerstreak@1: superclass = getlibrary(superclass) flickerstreak@1: arg = { ... } flickerstreak@1: for i, v in ipairs(arg) do flickerstreak@1: arg[i] = getlibrary(v) flickerstreak@1: end flickerstreak@1: if superclass then flickerstreak@1: if superclass.class then -- mixin flickerstreak@1: table.insert(arg, 1, superclass) flickerstreak@1: superclass = Class flickerstreak@1: end flickerstreak@1: else flickerstreak@1: superclass = Class flickerstreak@1: end flickerstreak@1: local key = getcomplexuid(superclass, unpack(arg)) flickerstreak@1: if not pool[key] then flickerstreak@1: local class = Class(superclass, unpack(arg)) flickerstreak@1: if not classmeta then flickerstreak@1: classmeta = {} flickerstreak@1: local mt = getmetatable(class) flickerstreak@1: for k,v in pairs(mt) do flickerstreak@1: classmeta[k] = v flickerstreak@1: end flickerstreak@1: classmeta.__newindex = newindex flickerstreak@1: end flickerstreak@1: -- Prevent the user from adding methods to this class. flickerstreak@1: -- NOTE: I'm not preventing modifications of existing class members, flickerstreak@1: -- but it's likely that only a truly malicious user will be doing so. flickerstreak@1: class.sealed = true flickerstreak@1: setmetatable(class, classmeta) flickerstreak@1: getmetatable(class.prototype).__newindex = protonewindex flickerstreak@1: pool[key] = class flickerstreak@1: end flickerstreak@1: return pool[key] flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: AceOO.Factory = Factory flickerstreak@1: AceOO.Object = Object flickerstreak@1: AceOO.Class = Class flickerstreak@1: AceOO.Mixin = Mixin flickerstreak@1: AceOO.Interface = Interface flickerstreak@1: AceOO.Classpool = Classpool flickerstreak@1: AceOO.inherits = inherits flickerstreak@1: flickerstreak@1: -- Library handling bits flickerstreak@1: flickerstreak@1: local function activate(self, oldLib, oldDeactivate) flickerstreak@1: AceOO = self flickerstreak@1: Factory = self.Factory flickerstreak@1: Object = self.Object flickerstreak@1: Class = self.Class flickerstreak@1: ClassFactory.prototype = Class flickerstreak@1: Mixin = self.Mixin flickerstreak@1: Interface = self.Interface flickerstreak@1: Classpool = self.Classpool flickerstreak@1: flickerstreak@1: if oldLib then flickerstreak@1: self.classes = oldLib.classes flickerstreak@1: end flickerstreak@1: if not self.classes then flickerstreak@1: self.classes = setmetatable({}, {__mode="k"}) flickerstreak@1: else flickerstreak@1: for class in pairs(self.classes) do flickerstreak@1: class.new = class_new flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: if oldDeactivate then flickerstreak@1: oldDeactivate(oldLib) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate) flickerstreak@1: AceOO = AceLibrary(MAJOR_VERSION)