Add files via upload
This commit is contained in:
@@ -0,0 +1,986 @@
|
||||
--[[
|
||||
Name: AceOO-2.0
|
||||
Revision: $Rev: 23383 $
|
||||
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
|
||||
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
|
||||
Website: http://www.wowace.com/
|
||||
Documentation: http://www.wowace.com/index.php/AceOO-2.0
|
||||
SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
|
||||
Description: Library to provide an object-orientation framework.
|
||||
Dependencies: AceLibrary
|
||||
License: MIT
|
||||
]]
|
||||
|
||||
local MAJOR_VERSION = "AceOO-2.0"
|
||||
local MINOR_VERSION = "$Revision: 23383 $"
|
||||
|
||||
-- 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 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 ("0"):rep(8 - cap:len()) .. cap
|
||||
end
|
||||
local function getuid(t)
|
||||
local mt = getmetatable(t)
|
||||
setmetatable(t, nil)
|
||||
local str = tostring(t)
|
||||
setmetatable(t, mt)
|
||||
local cap = str:match("[^:]*: 0x(.*)$")
|
||||
if not cap then
|
||||
cap = str:match("[^:]*: (.*)$")
|
||||
end
|
||||
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
|
||||
|
||||
local function deeprawget(self, k)
|
||||
while true do
|
||||
local v = rawget(self, k)
|
||||
if v ~= nil then
|
||||
return v
|
||||
end
|
||||
local mt = getmetatable(self)
|
||||
if not mt or type(mt.__index) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
self = mt.__index
|
||||
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 (...) 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 getlibraries(...)
|
||||
if select('#', ...) == 0 then
|
||||
return
|
||||
end
|
||||
return getlibrary((select(1, ...))), getlibraries(select(2, ...))
|
||||
end
|
||||
local arg = {}
|
||||
local function new(obj, ...)
|
||||
local t = {}
|
||||
local uid = getuid(t)
|
||||
obj:init(t, getlibraries(...))
|
||||
t.uid = uid
|
||||
return t
|
||||
end
|
||||
|
||||
local function createnew(self, ...)
|
||||
local o = self.prototype
|
||||
local x = new(o, getlibraries(...))
|
||||
return x
|
||||
end
|
||||
|
||||
function Factory(obj, newobj, ...)
|
||||
local t = new(obj, ...)
|
||||
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 = {
|
||||
__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', {})
|
||||
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
|
||||
local class = deeprawget(object, 'class')
|
||||
if class then
|
||||
current = 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 = deeprawget(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 = deeprawget(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 = deeprawget(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 (...) Arguments to pass to the object init function.
|
||||
-- @return The new object.
|
||||
--
|
||||
-- @method Class init
|
||||
-- @brief Initialize a new class.
|
||||
-- @param parent Superclass.
|
||||
-- @param (...) 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
|
||||
local class_new
|
||||
do
|
||||
Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
|
||||
Class.super = Object
|
||||
|
||||
local function protostring(t)
|
||||
return '<' .. tostring(t.class) .. ' prototype>'
|
||||
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, ...)
|
||||
if self.virtual then
|
||||
AceOO:error("Cannot instantiate a virtual class.")
|
||||
end
|
||||
|
||||
local o = self.prototype
|
||||
local newobj = {}
|
||||
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(...)
|
||||
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, ...)
|
||||
return self:new(...)
|
||||
end,
|
||||
}
|
||||
function Class:init(newclass, parent, ...)
|
||||
parent = parent or self
|
||||
|
||||
local total
|
||||
|
||||
if parent.class then
|
||||
total = { parent, ... }
|
||||
parent = self
|
||||
else
|
||||
total = { ... }
|
||||
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 v.__deprecated then
|
||||
AceOO:error(v.__deprecated)
|
||||
end
|
||||
if not newclass.mixins then
|
||||
newclass.mixins = {}
|
||||
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 = {}
|
||||
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 not newclass.interfaces then
|
||||
newclass.interfaces = {}
|
||||
end
|
||||
for interface in pairs(parent.interfaces) do
|
||||
newclass.interfaces[interface] = true
|
||||
end
|
||||
end
|
||||
for k in pairs(total) do
|
||||
total[k] = nil
|
||||
end
|
||||
|
||||
newclass.super = parent
|
||||
|
||||
newclass.prototype = setmetatable(total, {
|
||||
__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 = {}
|
||||
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(...)
|
||||
local x = ClassFactory:new(...)
|
||||
if AceOO.classes then
|
||||
AceOO.classes[x] = true
|
||||
end
|
||||
return x
|
||||
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)
|
||||
if self.__deprecated then
|
||||
AceOO:error(self.__deprecated)
|
||||
end
|
||||
local mt = getmetatable(target)
|
||||
setmetatable(target, nil)
|
||||
local err, msg = pcall(_Embed, self, nil, target)
|
||||
if not err then
|
||||
setmetatable(target, mt)
|
||||
AceOO:error(msg)
|
||||
return
|
||||
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
|
||||
setmetatable(target, mt)
|
||||
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({}, {__mode="k"})
|
||||
end
|
||||
end
|
||||
|
||||
function Mixin.prototype:init(export, ...)
|
||||
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 = #export
|
||||
for i = 1, num do
|
||||
local v = export[i]
|
||||
export[i] = nil
|
||||
export[v] = true
|
||||
end
|
||||
|
||||
local interfaces
|
||||
if select('#', ...) >= 1 then
|
||||
interfaces = { ... }
|
||||
for i,v in ipairs(interfaces) do
|
||||
v = getlibrary(v)
|
||||
interfaces[i] = v
|
||||
if not v.class or not inherits(v, Interface) then
|
||||
AceOO:error("Mixins can inherit only from interfaces")
|
||||
end
|
||||
end
|
||||
local num = #interfaces
|
||||
for i = 1, num do
|
||||
local v = interfaces[i]
|
||||
interfaces[i] = nil
|
||||
interfaces[v] = true
|
||||
end
|
||||
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 (...) Superinterfaces
|
||||
do
|
||||
Interface = Class()
|
||||
function Interface:ToString()
|
||||
if self.GetLibraryVersion then
|
||||
return (self:GetLibraryVersion())
|
||||
else
|
||||
return 'Instance'
|
||||
end
|
||||
end
|
||||
function Interface.prototype:init(interface, ...)
|
||||
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
|
||||
if select('#', ...) >= 1 then
|
||||
self.superinterfaces = { ... }
|
||||
for i,v in ipairs(self.superinterfaces) do
|
||||
v = getlibrary(v)
|
||||
self.superinterfaces[i] = v
|
||||
if not inherits(v, Interface) or not v.class then
|
||||
AceOO:error('Cannot provide a non-Interface to inherit from')
|
||||
end
|
||||
end
|
||||
local num = #self.superinterfaces
|
||||
for i = 1, num do
|
||||
local v = self.superinterfaces[i]
|
||||
self.superinterfaces[i] = nil
|
||||
self.superinterfaces[v] = true
|
||||
end
|
||||
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 protonewindex(k, v)
|
||||
AceOO:error('Attempt to modify a read-only class prototype.')
|
||||
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, ...)
|
||||
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
|
||||
end
|
||||
for i = 1, select('#', ...) do
|
||||
local m = select(i, ...)
|
||||
if m.uid then
|
||||
table.insert(t, m.uid)
|
||||
else
|
||||
AceOO:error("%s is not an appropriate mixin", ts(m))
|
||||
end
|
||||
end
|
||||
table.sort(t)
|
||||
local uid = table.concat(t, '')
|
||||
local num = #t
|
||||
for i = 1, num do
|
||||
t[i] = nil
|
||||
end
|
||||
return uid
|
||||
end
|
||||
local classmeta
|
||||
local arg = {}
|
||||
function Classpool(superclass, ...)
|
||||
local l = getlibrary
|
||||
superclass = getlibrary(superclass)
|
||||
arg = { ... }
|
||||
for i, v in ipairs(arg) do
|
||||
arg[i] = getlibrary(v)
|
||||
end
|
||||
if superclass then
|
||||
if superclass.class then -- mixin
|
||||
table.insert(arg, 1, superclass)
|
||||
superclass = Class
|
||||
end
|
||||
else
|
||||
superclass = Class
|
||||
end
|
||||
local key = getcomplexuid(superclass, unpack(arg))
|
||||
if not pool[key] then
|
||||
local class = Class(superclass, unpack(arg))
|
||||
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 = protonewindex
|
||||
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 oldLib then
|
||||
self.classes = oldLib.classes
|
||||
end
|
||||
if not self.classes then
|
||||
self.classes = setmetatable({}, {__mode="k"})
|
||||
else
|
||||
for class in pairs(self.classes) do
|
||||
class.new = class_new
|
||||
end
|
||||
end
|
||||
|
||||
if oldDeactivate then
|
||||
oldDeactivate(oldLib)
|
||||
end
|
||||
end
|
||||
|
||||
AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate)
|
||||
AceOO = AceLibrary(MAJOR_VERSION)
|
||||
Reference in New Issue
Block a user