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