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