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)