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