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