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) |