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)