annotate libs/AceOO-2.0/AceOO-2.0.lua @ 1:c11ca1d8ed91

Version 0.1
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:03:57 +0000
parents
children
rev   line source
flickerstreak@1 1 --[[
flickerstreak@1 2 Name: AceOO-2.0
flickerstreak@1 3 Revision: $Rev: 18708 $
flickerstreak@1 4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
flickerstreak@1 5 Inspired By: Ace 1.x by Turan (turan@gryphon.com)
flickerstreak@1 6 Website: http://www.wowace.com/
flickerstreak@1 7 Documentation: http://www.wowace.com/index.php/AceOO-2.0
flickerstreak@1 8 SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
flickerstreak@1 9 Description: Library to provide an object-orientation framework.
flickerstreak@1 10 Dependencies: AceLibrary
flickerstreak@1 11 ]]
flickerstreak@1 12
flickerstreak@1 13 local MAJOR_VERSION = "AceOO-2.0"
flickerstreak@1 14 local MINOR_VERSION = "$Revision: 18708 $"
flickerstreak@1 15
flickerstreak@1 16 -- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
flickerstreak@1 17 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
flickerstreak@1 18 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
flickerstreak@1 19
flickerstreak@1 20 local AceOO = {
flickerstreak@1 21 error = AceLibrary.error,
flickerstreak@1 22 argCheck = AceLibrary.argCheck
flickerstreak@1 23 }
flickerstreak@1 24
flickerstreak@1 25 -- @function getuid
flickerstreak@1 26 -- @brief Obtain a unique string identifier for the object in question.
flickerstreak@1 27 -- @param t The object to obtain the uid for.
flickerstreak@1 28 -- @return The uid string.
flickerstreak@1 29 local function pad(cap)
flickerstreak@1 30 return string.rep('0', 8 - string.len(cap)) .. cap
flickerstreak@1 31 end
flickerstreak@1 32 local function getuid(t)
flickerstreak@1 33 local mt = getmetatable(t)
flickerstreak@1 34 setmetatable(t, nil)
flickerstreak@1 35 local str = tostring(t)
flickerstreak@1 36 setmetatable(t, mt)
flickerstreak@1 37 local _,_,cap = string.find(str, '[^:]*: 0x(.*)$')
flickerstreak@1 38 if cap then return pad(cap) end
flickerstreak@1 39 _,_,cap = string.find(str, '[^:]*: (.*)$')
flickerstreak@1 40 if cap then return pad(cap) end
flickerstreak@1 41 end
flickerstreak@1 42
flickerstreak@1 43 local function getlibrary(o)
flickerstreak@1 44 if type(o) == "table" then
flickerstreak@1 45 return o
flickerstreak@1 46 elseif type(o) == "string" then
flickerstreak@1 47 if not AceLibrary:HasInstance(o) then
flickerstreak@1 48 AceOO:error("Library %q does not exist.", o)
flickerstreak@1 49 end
flickerstreak@1 50 return AceLibrary(o)
flickerstreak@1 51 end
flickerstreak@1 52 end
flickerstreak@1 53
flickerstreak@1 54 -- @function Factory
flickerstreak@1 55 -- @brief Construct a factory for the creation of objects.
flickerstreak@1 56 -- @param obj The object whose init method will be called on the new factory
flickerstreak@1 57 -- object.
flickerstreak@1 58 -- @param newobj The object whose init method will be called on the new
flickerstreak@1 59 -- objects that the Factory creates, to initialize them.
flickerstreak@1 60 -- @param (...) Arguments which will be passed to obj.init() in addition
flickerstreak@1 61 -- to the Factory object.
flickerstreak@1 62 -- @return The new factory which creates a newobj when its new method is called,
flickerstreak@1 63 -- or when it is called directly (__call metamethod).
flickerstreak@1 64 local Factory
flickerstreak@1 65 do
flickerstreak@1 66 local arg = {}
flickerstreak@1 67 local function new(obj, ...)
flickerstreak@1 68 local t = {}
flickerstreak@1 69 local uid = getuid(t)
flickerstreak@1 70 for i = 1, select('#', ...) do
flickerstreak@1 71 arg[i] = getlibrary(select(i, ...))
flickerstreak@1 72 end
flickerstreak@1 73 obj:init(t, unpack(arg))
flickerstreak@1 74 for i = 1, select('#', ...) do
flickerstreak@1 75 arg[i] = nil
flickerstreak@1 76 end
flickerstreak@1 77 t.uid = uid
flickerstreak@1 78 return t
flickerstreak@1 79 end
flickerstreak@1 80
flickerstreak@1 81 local function createnew(self, ...)
flickerstreak@1 82 local o = self.prototype
flickerstreak@1 83 for i = 1, select('#', ...) do
flickerstreak@1 84 arg[i] = getlibrary(select(i, ...))
flickerstreak@1 85 end
flickerstreak@1 86 local x = new(o, unpack(arg))
flickerstreak@1 87 for i = 1, select('#', ...) do
flickerstreak@1 88 arg[i] = nil
flickerstreak@1 89 end
flickerstreak@1 90 return x
flickerstreak@1 91 end
flickerstreak@1 92
flickerstreak@1 93 function Factory(obj, newobj, ...)
flickerstreak@1 94 local t = new(obj, ...)
flickerstreak@1 95 t.prototype = newobj
flickerstreak@1 96 t.new = createnew
flickerstreak@1 97 getmetatable(t).__call = t.new
flickerstreak@1 98 return t
flickerstreak@1 99 end
flickerstreak@1 100 end
flickerstreak@1 101
flickerstreak@1 102
flickerstreak@1 103 local function objtostring(self)
flickerstreak@1 104 if self.ToString then
flickerstreak@1 105 return self:ToString()
flickerstreak@1 106 elseif self.GetLibraryVersion then
flickerstreak@1 107 return (self:GetLibraryVersion())
flickerstreak@1 108 elseif self.super then
flickerstreak@1 109 local s = "Sub-" .. tostring(self.super)
flickerstreak@1 110 local first = true
flickerstreak@1 111 if self.interfaces then
flickerstreak@1 112 for interface in pairs(self.interfaces) do
flickerstreak@1 113 if first then
flickerstreak@1 114 s = s .. "(" .. tostring(interface)
flickerstreak@1 115 first = false
flickerstreak@1 116 else
flickerstreak@1 117 s = s .. ", " .. tostring(interface)
flickerstreak@1 118 end
flickerstreak@1 119 end
flickerstreak@1 120 end
flickerstreak@1 121 if self.mixins then
flickerstreak@1 122 for mixin in pairs(self.mixins) do
flickerstreak@1 123 if first then
flickerstreak@1 124 s = s .. tostring(mixin)
flickerstreak@1 125 first = false
flickerstreak@1 126 else
flickerstreak@1 127 s = s .. ", " .. tostring(mixin)
flickerstreak@1 128 end
flickerstreak@1 129 end
flickerstreak@1 130 end
flickerstreak@1 131 if first then
flickerstreak@1 132 if self.uid then
flickerstreak@1 133 return s .. ":" .. self.uid
flickerstreak@1 134 else
flickerstreak@1 135 return s
flickerstreak@1 136 end
flickerstreak@1 137 else
flickerstreak@1 138 return s .. ")"
flickerstreak@1 139 end
flickerstreak@1 140 else
flickerstreak@1 141 return self.uid and 'Subclass:' .. self.uid or 'Subclass'
flickerstreak@1 142 end
flickerstreak@1 143 end
flickerstreak@1 144
flickerstreak@1 145 -- @table Object
flickerstreak@1 146 -- @brief Base of all objects, including Class.
flickerstreak@1 147 --
flickerstreak@1 148 -- @method init
flickerstreak@1 149 -- @brief Initialize a new object.
flickerstreak@1 150 -- @param newobject The object to initialize
flickerstreak@1 151 -- @param class The class to make newobject inherit from
flickerstreak@1 152 local Object
flickerstreak@1 153 do
flickerstreak@1 154 Object = {}
flickerstreak@1 155 function Object:init(newobject, class)
flickerstreak@1 156 local parent = class or self
flickerstreak@1 157 if not rawget(newobject, 'uid') then
flickerstreak@1 158 newobject.uid = getuid(newobject)
flickerstreak@1 159 end
flickerstreak@1 160 local mt = {
flickerstreak@1 161 __index = parent,
flickerstreak@1 162 __tostring = objtostring,
flickerstreak@1 163 }
flickerstreak@1 164 setmetatable(newobject, mt)
flickerstreak@1 165 end
flickerstreak@1 166 Object.uid = getuid(Object)
flickerstreak@1 167 setmetatable(Object, { __tostring = function() return 'Object' end })
flickerstreak@1 168 end
flickerstreak@1 169
flickerstreak@1 170 local Interface
flickerstreak@1 171
flickerstreak@1 172 local function validateInterface(object, interface)
flickerstreak@1 173 if not object.class and object.prototype then
flickerstreak@1 174 object = object.prototype
flickerstreak@1 175 end
flickerstreak@1 176 for k,v in pairs(interface.interface) do
flickerstreak@1 177 if tostring(type(object[k])) ~= v then
flickerstreak@1 178 return false
flickerstreak@1 179 end
flickerstreak@1 180 end
flickerstreak@1 181 if interface.superinterfaces then
flickerstreak@1 182 for superinterface in pairs(interface.superinterfaces) do
flickerstreak@1 183 if not validateInterface(object, superinterface) then
flickerstreak@1 184 return false
flickerstreak@1 185 end
flickerstreak@1 186 end
flickerstreak@1 187 end
flickerstreak@1 188 if type(object.class) == "table" and rawequal(object.class.prototype, object) then
flickerstreak@1 189 if not object.class.interfaces then
flickerstreak@1 190 rawset(object.class, 'interfaces', {})
flickerstreak@1 191 end
flickerstreak@1 192 object.class.interfaces[interface] = true
flickerstreak@1 193 elseif type(object.class) == "table" and type(object.class.prototype) == "table" then
flickerstreak@1 194 validateInterface(object.class.prototype, interface)
flickerstreak@1 195 -- check if class is proper, thus preventing future checks.
flickerstreak@1 196 end
flickerstreak@1 197 return true
flickerstreak@1 198 end
flickerstreak@1 199
flickerstreak@1 200 -- @function inherits
flickerstreak@1 201 -- @brief Return whether an Object or Class inherits from a given
flickerstreak@1 202 -- parent.
flickerstreak@1 203 -- @param object Object or Class to check
flickerstreak@1 204 -- @param parent Parent to test inheritance from
flickerstreak@1 205 -- @return whether an Object or Class inherits from a given
flickerstreak@1 206 -- parent.
flickerstreak@1 207 local function inherits(object, parent)
flickerstreak@1 208 object = getlibrary(object)
flickerstreak@1 209 if type(parent) == "string" then
flickerstreak@1 210 if not AceLibrary:HasInstance(parent) then
flickerstreak@1 211 return false
flickerstreak@1 212 else
flickerstreak@1 213 parent = AceLibrary(parent)
flickerstreak@1 214 end
flickerstreak@1 215 end
flickerstreak@1 216 AceOO:argCheck(parent, 2, "table")
flickerstreak@1 217 if type(object) ~= "table" then
flickerstreak@1 218 return false
flickerstreak@1 219 end
flickerstreak@1 220 local current
flickerstreak@1 221 if object.class then
flickerstreak@1 222 current = object.class
flickerstreak@1 223 else
flickerstreak@1 224 current = object
flickerstreak@1 225 end
flickerstreak@1 226 if type(current) ~= "table" then
flickerstreak@1 227 return false
flickerstreak@1 228 end
flickerstreak@1 229 if rawequal(current, parent) then
flickerstreak@1 230 return true
flickerstreak@1 231 end
flickerstreak@1 232 if parent.class then
flickerstreak@1 233 while true do
flickerstreak@1 234 if rawequal(current, Object) then
flickerstreak@1 235 break
flickerstreak@1 236 end
flickerstreak@1 237 if current.mixins then
flickerstreak@1 238 for mixin in pairs(current.mixins) do
flickerstreak@1 239 if rawequal(mixin, parent) then
flickerstreak@1 240 return true
flickerstreak@1 241 end
flickerstreak@1 242 end
flickerstreak@1 243 end
flickerstreak@1 244 if current.interfaces then
flickerstreak@1 245 for interface in pairs(current.interfaces) do
flickerstreak@1 246 if rawequal(interface, parent) then
flickerstreak@1 247 return true
flickerstreak@1 248 end
flickerstreak@1 249 end
flickerstreak@1 250 end
flickerstreak@1 251 current = current.super
flickerstreak@1 252 if type(current) ~= "table" then
flickerstreak@1 253 break
flickerstreak@1 254 end
flickerstreak@1 255 end
flickerstreak@1 256
flickerstreak@1 257 local isInterface = false
flickerstreak@1 258 local curr = parent.class
flickerstreak@1 259 while true do
flickerstreak@1 260 if rawequal(curr, Object) then
flickerstreak@1 261 break
flickerstreak@1 262 elseif rawequal(curr, Interface) then
flickerstreak@1 263 isInterface = true
flickerstreak@1 264 break
flickerstreak@1 265 end
flickerstreak@1 266 curr = curr.super
flickerstreak@1 267 if type(curr) ~= "table" then
flickerstreak@1 268 break
flickerstreak@1 269 end
flickerstreak@1 270 end
flickerstreak@1 271 return isInterface and validateInterface(object, parent)
flickerstreak@1 272 else
flickerstreak@1 273 while true do
flickerstreak@1 274 if rawequal(current, parent) then
flickerstreak@1 275 return true
flickerstreak@1 276 elseif rawequal(current, Object) then
flickerstreak@1 277 return false
flickerstreak@1 278 end
flickerstreak@1 279 current = current.super
flickerstreak@1 280 if type(current) ~= "table" then
flickerstreak@1 281 return false
flickerstreak@1 282 end
flickerstreak@1 283 end
flickerstreak@1 284 end
flickerstreak@1 285 end
flickerstreak@1 286
flickerstreak@1 287 -- @table Class
flickerstreak@1 288 -- @brief An object factory which sets up inheritence and supports
flickerstreak@1 289 -- 'mixins'.
flickerstreak@1 290 --
flickerstreak@1 291 -- @metamethod Class call
flickerstreak@1 292 -- @brief Call ClassFactory:new() to create a new class.
flickerstreak@1 293 --
flickerstreak@1 294 -- @method Class new
flickerstreak@1 295 -- @brief Construct a new object.
flickerstreak@1 296 -- @param (...) Arguments to pass to the object init function.
flickerstreak@1 297 -- @return The new object.
flickerstreak@1 298 --
flickerstreak@1 299 -- @method Class init
flickerstreak@1 300 -- @brief Initialize a new class.
flickerstreak@1 301 -- @param parent Superclass.
flickerstreak@1 302 -- @param (...) Mixins.
flickerstreak@1 303 --
flickerstreak@1 304 -- @method Class ToString
flickerstreak@1 305 -- @return A string representing the object, in this case 'Class'.
flickerstreak@1 306 local initStatus
flickerstreak@1 307 local Class
flickerstreak@1 308 local Mixin
flickerstreak@1 309 local autoEmbed = false
flickerstreak@1 310 local function traverseInterfaces(bit, total)
flickerstreak@1 311 if bit.superinterfaces then
flickerstreak@1 312 for interface in pairs(bit.superinterfaces) do
flickerstreak@1 313 if not total[interface] then
flickerstreak@1 314 total[interface] = true
flickerstreak@1 315 traverseInterfaces(interface, total)
flickerstreak@1 316 end
flickerstreak@1 317 end
flickerstreak@1 318 end
flickerstreak@1 319 end
flickerstreak@1 320 local class_new
flickerstreak@1 321 do
flickerstreak@1 322 Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
flickerstreak@1 323 Class.super = Object
flickerstreak@1 324
flickerstreak@1 325 local function protostring(t)
flickerstreak@1 326 return '<' .. tostring(t.class) .. ' prototype>'
flickerstreak@1 327 end
flickerstreak@1 328 local function classobjectstring(t)
flickerstreak@1 329 if t.ToString then
flickerstreak@1 330 return t:ToString()
flickerstreak@1 331 elseif t.GetLibraryVersion then
flickerstreak@1 332 return (t:GetLibraryVersion())
flickerstreak@1 333 else
flickerstreak@1 334 return '<' .. tostring(t.class) .. ' instance>'
flickerstreak@1 335 end
flickerstreak@1 336 end
flickerstreak@1 337 local function classobjectequal(self, other)
flickerstreak@1 338 if type(self) == "table" and self.Equals then
flickerstreak@1 339 return self:Equals(other)
flickerstreak@1 340 elseif type(other) == "table" and other.Equals then
flickerstreak@1 341 return other:Equals(self)
flickerstreak@1 342 elseif type(self) == "table" and self.CompareTo then
flickerstreak@1 343 return self:CompareTo(other) == 0
flickerstreak@1 344 elseif type(other) == "table" and other.CompareTo then
flickerstreak@1 345 return other:CompareTo(self) == 0
flickerstreak@1 346 else
flickerstreak@1 347 return rawequal(self, other)
flickerstreak@1 348 end
flickerstreak@1 349 end
flickerstreak@1 350 local function classobjectlessthan(self, other)
flickerstreak@1 351 if type(self) == "table" and self.IsLessThan then
flickerstreak@1 352 return self:IsLessThan(other)
flickerstreak@1 353 elseif type(other) == "table" and other.IsLessThanOrEqualTo then
flickerstreak@1 354 return not other:IsLessThanOrEqualTo(self)
flickerstreak@1 355 elseif type(self) == "table" and self.CompareTo then
flickerstreak@1 356 return self:CompareTo(other) < 0
flickerstreak@1 357 elseif type(other) == "table" and other.CompareTo then
flickerstreak@1 358 return other:CompareTo(self) > 0
flickerstreak@1 359 elseif type(other) == "table" and other.IsLessThan and other.Equals then
flickerstreak@1 360 return other:Equals(self) or other:IsLessThan(self)
flickerstreak@1 361 else
flickerstreak@1 362 AceOO:error("cannot compare two objects")
flickerstreak@1 363 end
flickerstreak@1 364 end
flickerstreak@1 365 local function classobjectlessthanequal(self, other)
flickerstreak@1 366 if type(self) == "table" and self.IsLessThanOrEqualTo then
flickerstreak@1 367 return self:IsLessThanOrEqualTo(other)
flickerstreak@1 368 elseif type(other) == "table" and other.IsLessThan then
flickerstreak@1 369 return not other:IsLessThan(self)
flickerstreak@1 370 elseif type(self) == "table" and self.CompareTo then
flickerstreak@1 371 return self:CompareTo(other) <= 0
flickerstreak@1 372 elseif type(other) == "table" and other.CompareTo then
flickerstreak@1 373 return other:CompareTo(self) >= 0
flickerstreak@1 374 elseif type(self) == "table" and self.IsLessThan and self.Equals then
flickerstreak@1 375 return self:Equals(other) or self:IsLessThan(other)
flickerstreak@1 376 else
flickerstreak@1 377 AceOO:error("cannot compare two incompatible objects")
flickerstreak@1 378 end
flickerstreak@1 379 end
flickerstreak@1 380 local function classobjectadd(self, other)
flickerstreak@1 381 if type(self) == "table" and self.Add then
flickerstreak@1 382 return self:Add(other)
flickerstreak@1 383 else
flickerstreak@1 384 AceOO:error("cannot add two incompatible objects")
flickerstreak@1 385 end
flickerstreak@1 386 end
flickerstreak@1 387 local function classobjectsub(self, other)
flickerstreak@1 388 if type(self) == "table" and self.Subtract then
flickerstreak@1 389 return self:Subtract(other)
flickerstreak@1 390 else
flickerstreak@1 391 AceOO:error("cannot subtract two incompatible objects")
flickerstreak@1 392 end
flickerstreak@1 393 end
flickerstreak@1 394 local function classobjectunm(self, other)
flickerstreak@1 395 if type(self) == "table" and self.UnaryNegation then
flickerstreak@1 396 return self:UnaryNegation(other)
flickerstreak@1 397 else
flickerstreak@1 398 AceOO:error("attempt to negate an incompatible object")
flickerstreak@1 399 end
flickerstreak@1 400 end
flickerstreak@1 401 local function classobjectmul(self, other)
flickerstreak@1 402 if type(self) == "table" and self.Multiply then
flickerstreak@1 403 return self:Multiply(other)
flickerstreak@1 404 else
flickerstreak@1 405 AceOO:error("cannot multiply two incompatible objects")
flickerstreak@1 406 end
flickerstreak@1 407 end
flickerstreak@1 408 local function classobjectdiv(self, other)
flickerstreak@1 409 if type(self) == "table" and self.Divide then
flickerstreak@1 410 return self:Divide(other)
flickerstreak@1 411 else
flickerstreak@1 412 AceOO:error("cannot divide two incompatible objects")
flickerstreak@1 413 end
flickerstreak@1 414 end
flickerstreak@1 415 local function classobjectpow(self, other)
flickerstreak@1 416 if type(self) == "table" and self.Exponent then
flickerstreak@1 417 return self:Exponent(other)
flickerstreak@1 418 else
flickerstreak@1 419 AceOO:error("cannot exponentiate two incompatible objects")
flickerstreak@1 420 end
flickerstreak@1 421 end
flickerstreak@1 422 local function classobjectconcat(self, other)
flickerstreak@1 423 if type(self) == "table" and self.Concatenate then
flickerstreak@1 424 return self:Concatenate(other)
flickerstreak@1 425 else
flickerstreak@1 426 AceOO:error("cannot concatenate two incompatible objects")
flickerstreak@1 427 end
flickerstreak@1 428 end
flickerstreak@1 429 function class_new(self, ...)
flickerstreak@1 430 if self.virtual then
flickerstreak@1 431 AceOO:error("Cannot instantiate a virtual class.")
flickerstreak@1 432 end
flickerstreak@1 433
flickerstreak@1 434 local o = self.prototype
flickerstreak@1 435 local newobj = {}
flickerstreak@1 436 if o.class and o.class.instancemeta then
flickerstreak@1 437 setmetatable(newobj, o.class.instancemeta)
flickerstreak@1 438 else
flickerstreak@1 439 Object:init(newobj, o)
flickerstreak@1 440 end
flickerstreak@1 441
flickerstreak@1 442 if self.interfaces and not self.interfacesVerified then
flickerstreak@1 443 -- Verify the interfaces
flickerstreak@1 444
flickerstreak@1 445 for interface in pairs(self.interfaces) do
flickerstreak@1 446 for field,kind in pairs(interface.interface) do
flickerstreak@1 447 if tostring(type(newobj[field])) ~= kind then
flickerstreak@1 448 AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field])))
flickerstreak@1 449 end
flickerstreak@1 450 end
flickerstreak@1 451 end
flickerstreak@1 452 self.interfacesVerified = true
flickerstreak@1 453 end
flickerstreak@1 454 local tmp = initStatus
flickerstreak@1 455 initStatus = newobj
flickerstreak@1 456 newobj:init(...)
flickerstreak@1 457 if initStatus then
flickerstreak@1 458 initStatus = tmp
flickerstreak@1 459 AceOO:error("Initialization not completed, be sure to call the superclass's init method.")
flickerstreak@1 460 return
flickerstreak@1 461 end
flickerstreak@1 462 initStatus = tmp
flickerstreak@1 463 return newobj
flickerstreak@1 464 end
flickerstreak@1 465 local classmeta = {
flickerstreak@1 466 __tostring = objtostring,
flickerstreak@1 467 __call = function(self, ...)
flickerstreak@1 468 return self:new(...)
flickerstreak@1 469 end,
flickerstreak@1 470 }
flickerstreak@1 471 function Class:init(newclass, parent, ...)
flickerstreak@1 472 parent = parent or self
flickerstreak@1 473
flickerstreak@1 474 local total
flickerstreak@1 475
flickerstreak@1 476 if parent.class then
flickerstreak@1 477 total = { parent, ... }
flickerstreak@1 478 parent = self
flickerstreak@1 479 else
flickerstreak@1 480 total = { ... }
flickerstreak@1 481 end
flickerstreak@1 482 if not inherits(parent, Class) then
flickerstreak@1 483 AceOO:error("Classes must inherit from a proper class")
flickerstreak@1 484 end
flickerstreak@1 485 if parent.sealed then
flickerstreak@1 486 AceOO:error("Cannot inherit from a sealed class")
flickerstreak@1 487 end
flickerstreak@1 488 for i,v in ipairs(total) do
flickerstreak@1 489 if inherits(v, Mixin) and v.class then
flickerstreak@1 490 if v.__deprecated then
flickerstreak@1 491 AceOO:error(v.__deprecated)
flickerstreak@1 492 end
flickerstreak@1 493 if not newclass.mixins then
flickerstreak@1 494 newclass.mixins = {}
flickerstreak@1 495 end
flickerstreak@1 496 if newclass.mixins[v] then
flickerstreak@1 497 AceOO:error("Cannot explicitly inherit from the same mixin twice")
flickerstreak@1 498 end
flickerstreak@1 499 newclass.mixins[v] = true
flickerstreak@1 500 elseif inherits(v, Interface) and v.class then
flickerstreak@1 501 if not newclass.interfaces then
flickerstreak@1 502 newclass.interfaces = {}
flickerstreak@1 503 end
flickerstreak@1 504 if newclass.interfaces[v] then
flickerstreak@1 505 AceOO:error("Cannot explicitly inherit from the same interface twice")
flickerstreak@1 506 end
flickerstreak@1 507 newclass.interfaces[v] = true
flickerstreak@1 508 else
flickerstreak@1 509 AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
flickerstreak@1 510 end
flickerstreak@1 511 end
flickerstreak@1 512 if parent.interfaces then
flickerstreak@1 513 if not newclass.interfaces then
flickerstreak@1 514 newclass.interfaces = {}
flickerstreak@1 515 end
flickerstreak@1 516 for interface in pairs(parent.interfaces) do
flickerstreak@1 517 newclass.interfaces[interface] = true
flickerstreak@1 518 end
flickerstreak@1 519 end
flickerstreak@1 520 for k in pairs(total) do
flickerstreak@1 521 total[k] = nil
flickerstreak@1 522 end
flickerstreak@1 523
flickerstreak@1 524 newclass.super = parent
flickerstreak@1 525
flickerstreak@1 526 newclass.prototype = setmetatable(total, {
flickerstreak@1 527 __index = parent.prototype,
flickerstreak@1 528 __tostring = protostring,
flickerstreak@1 529 })
flickerstreak@1 530 total = nil
flickerstreak@1 531
flickerstreak@1 532 newclass.instancemeta = {
flickerstreak@1 533 __index = newclass.prototype,
flickerstreak@1 534 __tostring = classobjectstring,
flickerstreak@1 535 __eq = classobjectequal,
flickerstreak@1 536 __lt = classobjectlessthan,
flickerstreak@1 537 __le = classobjectlessthanequal,
flickerstreak@1 538 __add = classobjectadd,
flickerstreak@1 539 __sub = classobjectsub,
flickerstreak@1 540 __unm = classobjectunm,
flickerstreak@1 541 __mul = classobjectmul,
flickerstreak@1 542 __div = classobjectdiv,
flickerstreak@1 543 __pow = classobjectpow,
flickerstreak@1 544 __concat = classobjectconcat,
flickerstreak@1 545 }
flickerstreak@1 546
flickerstreak@1 547 setmetatable(newclass, classmeta)
flickerstreak@1 548
flickerstreak@1 549 newclass.new = class_new
flickerstreak@1 550
flickerstreak@1 551 if newclass.mixins then
flickerstreak@1 552 -- Fold in the mixins
flickerstreak@1 553 local err, msg
flickerstreak@1 554 for mixin in pairs(newclass.mixins) do
flickerstreak@1 555 local ret
flickerstreak@1 556 autoEmbed = true
flickerstreak@1 557 ret, msg = pcall(mixin.embed, mixin, newclass.prototype)
flickerstreak@1 558 autoEmbed = false
flickerstreak@1 559 if not ret then
flickerstreak@1 560 err = true
flickerstreak@1 561 break
flickerstreak@1 562 end
flickerstreak@1 563 end
flickerstreak@1 564
flickerstreak@1 565 if err then
flickerstreak@1 566 local pt = newclass.prototype
flickerstreak@1 567 for k,v in pairs(pt) do
flickerstreak@1 568 pt[k] = nil
flickerstreak@1 569 end
flickerstreak@1 570
flickerstreak@1 571 -- method conflict
flickerstreak@1 572 AceOO:error(msg)
flickerstreak@1 573 end
flickerstreak@1 574 end
flickerstreak@1 575
flickerstreak@1 576 newclass.prototype.class = newclass
flickerstreak@1 577
flickerstreak@1 578 if newclass.interfaces then
flickerstreak@1 579 for interface in pairs(newclass.interfaces) do
flickerstreak@1 580 traverseInterfaces(interface, newclass.interfaces)
flickerstreak@1 581 end
flickerstreak@1 582 end
flickerstreak@1 583 if newclass.mixins then
flickerstreak@1 584 for mixin in pairs(newclass.mixins) do
flickerstreak@1 585 if mixin.interfaces then
flickerstreak@1 586 if not newclass.interfaces then
flickerstreak@1 587 newclass.interfaces = {}
flickerstreak@1 588 end
flickerstreak@1 589 for interface in pairs(mixin.interfaces) do
flickerstreak@1 590 newclass.interfaces[interface] = true
flickerstreak@1 591 end
flickerstreak@1 592 end
flickerstreak@1 593 end
flickerstreak@1 594 end
flickerstreak@1 595 end
flickerstreak@1 596 function Class:ToString()
flickerstreak@1 597 if type(self.GetLibraryVersion) == "function" then
flickerstreak@1 598 return (self:GetLibraryVersion())
flickerstreak@1 599 else
flickerstreak@1 600 return "Class"
flickerstreak@1 601 end
flickerstreak@1 602 end
flickerstreak@1 603
flickerstreak@1 604 local tmp
flickerstreak@1 605 function Class.prototype:init()
flickerstreak@1 606 if rawequal(self, initStatus) then
flickerstreak@1 607 initStatus = nil
flickerstreak@1 608 else
flickerstreak@1 609 AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2)
flickerstreak@1 610 end
flickerstreak@1 611 self.uid = getuid(self)
flickerstreak@1 612 local current = self.class
flickerstreak@1 613 while true do
flickerstreak@1 614 if current == Class then
flickerstreak@1 615 break
flickerstreak@1 616 end
flickerstreak@1 617 if current.mixins then
flickerstreak@1 618 for mixin in pairs(current.mixins) do
flickerstreak@1 619 if type(mixin.OnInstanceInit) == "function" then
flickerstreak@1 620 mixin:OnInstanceInit(self)
flickerstreak@1 621 end
flickerstreak@1 622 end
flickerstreak@1 623 end
flickerstreak@1 624 current = current.super
flickerstreak@1 625 end
flickerstreak@1 626 end
flickerstreak@1 627 end
flickerstreak@1 628
flickerstreak@1 629
flickerstreak@1 630 -- @object ClassFactory
flickerstreak@1 631 -- @brief A factory for creating classes. Rarely used directly.
flickerstreak@1 632 local ClassFactory = Factory(Object, Class, Object)
flickerstreak@1 633
flickerstreak@1 634 function Class:new(...)
flickerstreak@1 635 local x = ClassFactory:new(...)
flickerstreak@1 636 if AceOO.classes then
flickerstreak@1 637 AceOO.classes[x] = true
flickerstreak@1 638 end
flickerstreak@1 639 return x
flickerstreak@1 640 end
flickerstreak@1 641 getmetatable(Class).__call = Class.new
flickerstreak@1 642
flickerstreak@1 643 -- @class Mixin
flickerstreak@1 644 -- @brief A class to create mixin objects, which contain methods that get
flickerstreak@1 645 -- "mixed in" to class prototypes.
flickerstreak@1 646 --
flickerstreak@1 647 -- @object Mixin prototype
flickerstreak@1 648 -- @brief The prototype that mixin objects inherit their methods from.
flickerstreak@1 649 --
flickerstreak@1 650 -- @method Mixin prototype embed
flickerstreak@1 651 -- @brief Mix in the methods of our object which are listed in our interface
flickerstreak@1 652 -- to the supplied target table.
flickerstreak@1 653 --
flickerstreak@1 654 -- @method Mixin prototype init
flickerstreak@1 655 -- @brief Initialize the mixin object.
flickerstreak@1 656 -- @param newobj The new object we're initializing.
flickerstreak@1 657 -- @param interface The interface we implement (the list of methods our
flickerstreak@1 658 -- prototype provides which should be mixed into the target
flickerstreak@1 659 -- table by embed).
flickerstreak@1 660 do
flickerstreak@1 661 Mixin = Class()
flickerstreak@1 662 function Mixin:ToString()
flickerstreak@1 663 if self.GetLibraryVersion then
flickerstreak@1 664 return (self:GetLibraryVersion())
flickerstreak@1 665 else
flickerstreak@1 666 return 'Mixin'
flickerstreak@1 667 end
flickerstreak@1 668 end
flickerstreak@1 669 local function _Embed(state, field, target)
flickerstreak@1 670 field = next(state.export, field)
flickerstreak@1 671 if field == nil then
flickerstreak@1 672 return
flickerstreak@1 673 end
flickerstreak@1 674
flickerstreak@1 675 if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
flickerstreak@1 676 AceOO:error("Method conflict in attempt to mixin. Field %q", field)
flickerstreak@1 677 end
flickerstreak@1 678
flickerstreak@1 679 target[field] = state[field]
flickerstreak@1 680
flickerstreak@1 681 local ret,msg = pcall(_Embed, state, field, target)
flickerstreak@1 682 if not ret then
flickerstreak@1 683 -- Mix in the next method according to the defined interface. If that
flickerstreak@1 684 -- fails due to a conflict, re-raise to back out the previous mixed
flickerstreak@1 685 -- methods.
flickerstreak@1 686
flickerstreak@1 687 target[field] = nil
flickerstreak@1 688 AceOO:error(msg)
flickerstreak@1 689 end
flickerstreak@1 690 end
flickerstreak@1 691 function Mixin.prototype:embed(target)
flickerstreak@1 692 if self.__deprecated then
flickerstreak@1 693 AceOO:error(self.__deprecated)
flickerstreak@1 694 end
flickerstreak@1 695 local mt = getmetatable(target)
flickerstreak@1 696 setmetatable(target, nil)
flickerstreak@1 697 local err, msg = pcall(_Embed, self, nil, target)
flickerstreak@1 698 if not err then
flickerstreak@1 699 setmetatable(target, mt)
flickerstreak@1 700 AceOO:error(msg)
flickerstreak@1 701 return
flickerstreak@1 702 end
flickerstreak@1 703 if type(self.embedList) == "table" then
flickerstreak@1 704 self.embedList[target] = true
flickerstreak@1 705 end
flickerstreak@1 706 if type(target.class) ~= "table" then
flickerstreak@1 707 target[self] = true
flickerstreak@1 708 end
flickerstreak@1 709 if not autoEmbed and type(self.OnManualEmbed) == "function" then
flickerstreak@1 710 self:OnManualEmbed(target)
flickerstreak@1 711 end
flickerstreak@1 712 setmetatable(target, mt)
flickerstreak@1 713 end
flickerstreak@1 714
flickerstreak@1 715 function Mixin.prototype:activate(oldLib, oldDeactivate)
flickerstreak@1 716 if oldLib and oldLib.embedList then
flickerstreak@1 717 for target in pairs(oldLib.embedList) do
flickerstreak@1 718 local mt = getmetatable(target)
flickerstreak@1 719 setmetatable(target, nil)
flickerstreak@1 720 for field in pairs(oldLib.export) do
flickerstreak@1 721 target[field] = nil
flickerstreak@1 722 end
flickerstreak@1 723 setmetatable(target, mt)
flickerstreak@1 724 end
flickerstreak@1 725 self.embedList = oldLib.embedList
flickerstreak@1 726 for target in pairs(self.embedList) do
flickerstreak@1 727 self:embed(target)
flickerstreak@1 728 end
flickerstreak@1 729 else
flickerstreak@1 730 self.embedList = setmetatable({}, {__mode="k"})
flickerstreak@1 731 end
flickerstreak@1 732 end
flickerstreak@1 733
flickerstreak@1 734 function Mixin.prototype:init(export, ...)
flickerstreak@1 735 AceOO:argCheck(export, 2, "table")
flickerstreak@1 736 for k,v in pairs(export) do
flickerstreak@1 737 if type(k) ~= "number" then
flickerstreak@1 738 AceOO:error("All keys to argument #2 must be numbers.")
flickerstreak@1 739 elseif type(v) ~= "string" then
flickerstreak@1 740 AceOO:error("All values to argument #2 must be strings.")
flickerstreak@1 741 end
flickerstreak@1 742 end
flickerstreak@1 743 local num = #export
flickerstreak@1 744 for i = 1, num do
flickerstreak@1 745 local v = export[i]
flickerstreak@1 746 export[i] = nil
flickerstreak@1 747 export[v] = true
flickerstreak@1 748 end
flickerstreak@1 749
flickerstreak@1 750 local interfaces
flickerstreak@1 751 if select('#', ...) >= 1 then
flickerstreak@1 752 interfaces = { ... }
flickerstreak@1 753 for i,v in ipairs(interfaces) do
flickerstreak@1 754 v = getlibrary(v)
flickerstreak@1 755 interfaces[i] = v
flickerstreak@1 756 if not v.class or not inherits(v, Interface) then
flickerstreak@1 757 AceOO:error("Mixins can inherit only from interfaces")
flickerstreak@1 758 end
flickerstreak@1 759 end
flickerstreak@1 760 local num = #interfaces
flickerstreak@1 761 for i = 1, num do
flickerstreak@1 762 local v = interfaces[i]
flickerstreak@1 763 interfaces[i] = nil
flickerstreak@1 764 interfaces[v] = true
flickerstreak@1 765 end
flickerstreak@1 766 for interface in pairs(interfaces) do
flickerstreak@1 767 traverseInterfaces(interface, interfaces)
flickerstreak@1 768 end
flickerstreak@1 769 for interface in pairs(interfaces) do
flickerstreak@1 770 for field,kind in pairs(interface.interface) do
flickerstreak@1 771 if kind ~= "nil" then
flickerstreak@1 772 local good = false
flickerstreak@1 773 for bit in pairs(export) do
flickerstreak@1 774 if bit == field then
flickerstreak@1 775 good = true
flickerstreak@1 776 break
flickerstreak@1 777 end
flickerstreak@1 778 end
flickerstreak@1 779 if not good then
flickerstreak@1 780 AceOO:error("Mixin does not fully accommodate field %q", field)
flickerstreak@1 781 end
flickerstreak@1 782 end
flickerstreak@1 783 end
flickerstreak@1 784 end
flickerstreak@1 785 end
flickerstreak@1 786 self.super = Mixin.prototype
flickerstreak@1 787 Mixin.super.prototype.init(self)
flickerstreak@1 788 self.export = export
flickerstreak@1 789 self.interfaces = interfaces
flickerstreak@1 790 end
flickerstreak@1 791 end
flickerstreak@1 792
flickerstreak@1 793 -- @class Interface
flickerstreak@1 794 -- @brief A class to create interfaces, which contain contracts that classes
flickerstreak@1 795 -- which inherit from this must comply with.
flickerstreak@1 796 --
flickerstreak@1 797 -- @object Interface prototype
flickerstreak@1 798 -- @brief The prototype that interface objects must adhere to.
flickerstreak@1 799 --
flickerstreak@1 800 -- @method Interface prototype init
flickerstreak@1 801 -- @brief Initialize the mixin object.
flickerstreak@1 802 -- @param interface The interface we contract (the hash of fields forced).
flickerstreak@1 803 -- @param (...) Superinterfaces
flickerstreak@1 804 do
flickerstreak@1 805 Interface = Class()
flickerstreak@1 806 function Interface:ToString()
flickerstreak@1 807 if self.GetLibraryVersion then
flickerstreak@1 808 return (self:GetLibraryVersion())
flickerstreak@1 809 else
flickerstreak@1 810 return 'Instance'
flickerstreak@1 811 end
flickerstreak@1 812 end
flickerstreak@1 813 function Interface.prototype:init(interface, ...)
flickerstreak@1 814 Interface.super.prototype.init(self)
flickerstreak@1 815 AceOO:argCheck(interface, 2, "table")
flickerstreak@1 816 for k,v in pairs(interface) do
flickerstreak@1 817 if type(k) ~= "string" then
flickerstreak@1 818 AceOO:error("All keys to argument #2 must be numbers.")
flickerstreak@1 819 elseif type(v) ~= "string" then
flickerstreak@1 820 AceOO:error("All values to argument #2 must be strings.")
flickerstreak@1 821 elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then
flickerstreak@1 822 AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".')
flickerstreak@1 823 end
flickerstreak@1 824 end
flickerstreak@1 825 if select('#', ...) >= 1 then
flickerstreak@1 826 self.superinterfaces = { ... }
flickerstreak@1 827 for i,v in ipairs(self.superinterfaces) do
flickerstreak@1 828 v = getlibrary(v)
flickerstreak@1 829 self.superinterfaces[i] = v
flickerstreak@1 830 if not inherits(v, Interface) or not v.class then
flickerstreak@1 831 AceOO:error('Cannot provide a non-Interface to inherit from')
flickerstreak@1 832 end
flickerstreak@1 833 end
flickerstreak@1 834 local num = #self.superinterfaces
flickerstreak@1 835 for i = 1, num do
flickerstreak@1 836 local v = self.superinterfaces[i]
flickerstreak@1 837 self.superinterfaces[i] = nil
flickerstreak@1 838 self.superinterfaces[v] = true
flickerstreak@1 839 end
flickerstreak@1 840 end
flickerstreak@1 841 self.interface = interface
flickerstreak@1 842 end
flickerstreak@1 843 end
flickerstreak@1 844
flickerstreak@1 845 -- @function Classpool
flickerstreak@1 846 -- @brief Obtain a read only class from our pool of classes, indexed by the
flickerstreak@1 847 -- superclass and mixins.
flickerstreak@1 848 -- @param sc The superclass of the class we want.
flickerstreak@1 849 -- @param (m1..m20) Mixins of the class we want's objects.
flickerstreak@1 850 -- @return A read only class from the class pool.
flickerstreak@1 851 local Classpool
flickerstreak@1 852 do
flickerstreak@1 853 local pool = setmetatable({}, {__mode = 'v'})
flickerstreak@1 854 local function newindex(k, v)
flickerstreak@1 855 AceOO:error('Attempt to modify a read-only class.')
flickerstreak@1 856 end
flickerstreak@1 857 local function protonewindex(k, v)
flickerstreak@1 858 AceOO:error('Attempt to modify a read-only class prototype.')
flickerstreak@1 859 end
flickerstreak@1 860 local function ts(bit)
flickerstreak@1 861 if type(bit) ~= "table" then
flickerstreak@1 862 return tostring(bit)
flickerstreak@1 863 elseif getmetatable(bit) and bit.__tostring then
flickerstreak@1 864 return tostring(bit)
flickerstreak@1 865 elseif type(bit.GetLibraryVersion) == "function" then
flickerstreak@1 866 return bit:GetLibraryVersion()
flickerstreak@1 867 else
flickerstreak@1 868 return tostring(bit)
flickerstreak@1 869 end
flickerstreak@1 870 end
flickerstreak@1 871 local t = {}
flickerstreak@1 872 local function getcomplexuid(sc, ...)
flickerstreak@1 873 if sc then
flickerstreak@1 874 if sc.uid then
flickerstreak@1 875 table.insert(t, sc.uid)
flickerstreak@1 876 else
flickerstreak@1 877 AceOO:error("%s is not an appropriate class/mixin", ts(sc))
flickerstreak@1 878 end
flickerstreak@1 879 end
flickerstreak@1 880 for i = 1, select('#', ...) do
flickerstreak@1 881 local m = select(i, ...)
flickerstreak@1 882 if m.uid then
flickerstreak@1 883 table.insert(t, m.uid)
flickerstreak@1 884 else
flickerstreak@1 885 AceOO:error("%s is not an appropriate mixin", ts(m))
flickerstreak@1 886 end
flickerstreak@1 887 end
flickerstreak@1 888 table.sort(t)
flickerstreak@1 889 local uid = table.concat(t, '')
flickerstreak@1 890 local num = #t
flickerstreak@1 891 for i = 1, num do
flickerstreak@1 892 t[i] = nil
flickerstreak@1 893 end
flickerstreak@1 894 return uid
flickerstreak@1 895 end
flickerstreak@1 896 local classmeta
flickerstreak@1 897 local arg = {}
flickerstreak@1 898 function Classpool(superclass, ...)
flickerstreak@1 899 local l = getlibrary
flickerstreak@1 900 superclass = getlibrary(superclass)
flickerstreak@1 901 arg = { ... }
flickerstreak@1 902 for i, v in ipairs(arg) do
flickerstreak@1 903 arg[i] = getlibrary(v)
flickerstreak@1 904 end
flickerstreak@1 905 if superclass then
flickerstreak@1 906 if superclass.class then -- mixin
flickerstreak@1 907 table.insert(arg, 1, superclass)
flickerstreak@1 908 superclass = Class
flickerstreak@1 909 end
flickerstreak@1 910 else
flickerstreak@1 911 superclass = Class
flickerstreak@1 912 end
flickerstreak@1 913 local key = getcomplexuid(superclass, unpack(arg))
flickerstreak@1 914 if not pool[key] then
flickerstreak@1 915 local class = Class(superclass, unpack(arg))
flickerstreak@1 916 if not classmeta then
flickerstreak@1 917 classmeta = {}
flickerstreak@1 918 local mt = getmetatable(class)
flickerstreak@1 919 for k,v in pairs(mt) do
flickerstreak@1 920 classmeta[k] = v
flickerstreak@1 921 end
flickerstreak@1 922 classmeta.__newindex = newindex
flickerstreak@1 923 end
flickerstreak@1 924 -- Prevent the user from adding methods to this class.
flickerstreak@1 925 -- NOTE: I'm not preventing modifications of existing class members,
flickerstreak@1 926 -- but it's likely that only a truly malicious user will be doing so.
flickerstreak@1 927 class.sealed = true
flickerstreak@1 928 setmetatable(class, classmeta)
flickerstreak@1 929 getmetatable(class.prototype).__newindex = protonewindex
flickerstreak@1 930 pool[key] = class
flickerstreak@1 931 end
flickerstreak@1 932 return pool[key]
flickerstreak@1 933 end
flickerstreak@1 934 end
flickerstreak@1 935
flickerstreak@1 936 AceOO.Factory = Factory
flickerstreak@1 937 AceOO.Object = Object
flickerstreak@1 938 AceOO.Class = Class
flickerstreak@1 939 AceOO.Mixin = Mixin
flickerstreak@1 940 AceOO.Interface = Interface
flickerstreak@1 941 AceOO.Classpool = Classpool
flickerstreak@1 942 AceOO.inherits = inherits
flickerstreak@1 943
flickerstreak@1 944 -- Library handling bits
flickerstreak@1 945
flickerstreak@1 946 local function activate(self, oldLib, oldDeactivate)
flickerstreak@1 947 AceOO = self
flickerstreak@1 948 Factory = self.Factory
flickerstreak@1 949 Object = self.Object
flickerstreak@1 950 Class = self.Class
flickerstreak@1 951 ClassFactory.prototype = Class
flickerstreak@1 952 Mixin = self.Mixin
flickerstreak@1 953 Interface = self.Interface
flickerstreak@1 954 Classpool = self.Classpool
flickerstreak@1 955
flickerstreak@1 956 if oldLib then
flickerstreak@1 957 self.classes = oldLib.classes
flickerstreak@1 958 end
flickerstreak@1 959 if not self.classes then
flickerstreak@1 960 self.classes = setmetatable({}, {__mode="k"})
flickerstreak@1 961 else
flickerstreak@1 962 for class in pairs(self.classes) do
flickerstreak@1 963 class.new = class_new
flickerstreak@1 964 end
flickerstreak@1 965 end
flickerstreak@1 966
flickerstreak@1 967 if oldDeactivate then
flickerstreak@1 968 oldDeactivate(oldLib)
flickerstreak@1 969 end
flickerstreak@1 970 end
flickerstreak@1 971
flickerstreak@1 972 AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate)
flickerstreak@1 973 AceOO = AceLibrary(MAJOR_VERSION)