vanilla-wow-addons – Blame information for rev 1

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