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