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: 8313 $
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: 8313 $"
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 local class_new
326 do
327 Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
328 Class.super = Object
329  
330 local function protostring(t)
331 return '<' .. tostring(t.class) .. ' prototype>'
332 end
333 local function classobjectstring(t)
334 if t.ToString then
335 return t:ToString()
336 elseif t.GetLibraryVersion then
337 return (t:GetLibraryVersion())
338 else
339 return '<' .. tostring(t.class) .. ' instance>'
340 end
341 end
342 local function classobjectequal(self, other)
343 if type(self) == "table" and self.Equals then
344 return self:Equals(other)
345 elseif type(other) == "table" and other.Equals then
346 return other:Equals(self)
347 elseif type(self) == "table" and self.CompareTo then
348 return self:CompareTo(other) == 0
349 elseif type(other) == "table" and other.CompareTo then
350 return other:CompareTo(self) == 0
351 else
352 return rawequal(self, other)
353 end
354 end
355 local function classobjectlessthan(self, other)
356 if type(self) == "table" and self.IsLessThan then
357 return self:IsLessThan(other)
358 elseif type(other) == "table" and other.IsLessThanOrEqualTo then
359 return not other:IsLessThanOrEqualTo(self)
360 elseif type(self) == "table" and self.CompareTo then
361 return self:CompareTo(other) < 0
362 elseif type(other) == "table" and other.CompareTo then
363 return other:CompareTo(self) > 0
364 elseif type(other) == "table" and other.IsLessThan and other.Equals then
365 return other:Equals(self) or other:IsLessThan(self)
366 else
367 AceOO:error("cannot compare two objects")
368 end
369 end
370 local function classobjectlessthanequal(self, other)
371 if type(self) == "table" and self.IsLessThanOrEqualTo then
372 return self:IsLessThanOrEqualTo(other)
373 elseif type(other) == "table" and other.IsLessThan then
374 return not other:IsLessThan(self)
375 elseif type(self) == "table" and self.CompareTo then
376 return self:CompareTo(other) <= 0
377 elseif type(other) == "table" and other.CompareTo then
378 return other:CompareTo(self) >= 0
379 elseif type(self) == "table" and self.IsLessThan and self.Equals then
380 return self:Equals(other) or self:IsLessThan(other)
381 else
382 AceOO:error("cannot compare two incompatible objects")
383 end
384 end
385 local function classobjectadd(self, other)
386 if type(self) == "table" and self.Add then
387 return self:Add(other)
388 else
389 AceOO:error("cannot add two incompatible objects")
390 end
391 end
392 local function classobjectsub(self, other)
393 if type(self) == "table" and self.Subtract then
394 return self:Subtract(other)
395 else
396 AceOO:error("cannot subtract two incompatible objects")
397 end
398 end
399 local function classobjectunm(self, other)
400 if type(self) == "table" and self.UnaryNegation then
401 return self:UnaryNegation(other)
402 else
403 AceOO:error("attempt to negate an incompatible object")
404 end
405 end
406 local function classobjectmul(self, other)
407 if type(self) == "table" and self.Multiply then
408 return self:Multiply(other)
409 else
410 AceOO:error("cannot multiply two incompatible objects")
411 end
412 end
413 local function classobjectdiv(self, other)
414 if type(self) == "table" and self.Divide then
415 return self:Divide(other)
416 else
417 AceOO:error("cannot divide two incompatible objects")
418 end
419 end
420 local function classobjectpow(self, other)
421 if type(self) == "table" and self.Exponent then
422 return self:Exponent(other)
423 else
424 AceOO:error("cannot exponentiate two incompatible objects")
425 end
426 end
427 local function classobjectconcat(self, other)
428 if type(self) == "table" and self.Concatenate then
429 return self:Concatenate(other)
430 else
431 AceOO:error("cannot concatenate two incompatible objects")
432 end
433 end
434 function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
435 a13, a14, a15, a16, a17, a18, a19, a20)
436 if self.virtual then
437 AceOO:error("Cannot instantiate a virtual class.")
438 end
439  
440 local o = self.prototype
441 local newobj = Compost and Compost:Acquire() or {}
442 if o.class and o.class.instancemeta then
443 setmetatable(newobj, o.class.instancemeta)
444 else
445 Object:init(newobj, o)
446 end
447  
448 if self.interfaces and not self.interfacesVerified then
449 -- Verify the interfaces
450  
451 for interface in pairs(self.interfaces) do
452 for field,kind in pairs(interface.interface) do
453 if tostring(type(newobj[field])) ~= kind then
454 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])))
455 end
456 end
457 end
458 self.interfacesVerified = true
459 end
460 local tmp = initStatus
461 initStatus = newobj
462 newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
463 a13, a14, a15, a16, a17, a18, a19, a20)
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, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
475 a13, a14, a15, a16, a17, a18, a19, a20)
476 return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
477 a13, a14, a15, a16, a17, a18, a19, a20)
478 end,
479 }
480 function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
481 a10, a11, a12, a13, a14, a15, a16,
482 a17, a18, a19, a20)
483 parent = parent or self
484  
485 local total
486  
487 if parent.class then
488 total = {
489 parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
490 a10, a11, a12, a13, a14, a15, a16,
491 a17, a18, a19, a20
492 }
493 parent = self
494 else
495 total = {
496 a1, a2, a3, a4, a5, a6, a7, a8, a9,
497 a10, a11, a12, a13, a14, a15, a16,
498 a17, a18, a19, a20
499 }
500 end
501 if not inherits(parent, Class) then
502 AceOO:error("Classes must inherit from a proper class")
503 end
504 if parent.sealed then
505 AceOO:error("Cannot inherit from a sealed class")
506 end
507 for i,v in ipairs(total) do
508 if inherits(v, Mixin) and v.class then
509 if not newclass.mixins then
510 newclass.mixins = Compost and Compost:Acquire() or {}
511 end
512 if newclass.mixins[v] then
513 AceOO:error("Cannot explicitly inherit from the same mixin twice")
514 end
515 newclass.mixins[v] = true
516 elseif inherits(v, Interface) and v.class then
517 if not newclass.interfaces then
518 newclass.interfaces = Compost and Compost:Acquire() or {}
519 end
520 if newclass.interfaces[v] then
521 AceOO:error("Cannot explicitly inherit from the same interface twice")
522 end
523 newclass.interfaces[v] = true
524 else
525 AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
526 end
527 end
528 if parent.interfaces then
529 if newclass.interfaces then
530 for interface in pairs(parent.interfaces) do
531 newclass.interfaces[interface] = true
532 end
533 else
534 newclass.interfaces = parent.interfaces
535 end
536 end
537 for k in pairs(total) do
538 total[k] = nil
539 end
540 table.setn(total, 0)
541  
542 newclass.super = parent
543  
544 newclass.prototype = setmetatable(total, Compost and Compost:AcquireHash(
545 '__index', parent.prototype,
546 '__tostring', protostring
547 ) or {
548 __index = parent.prototype,
549 __tostring = protostring,
550 })
551 total = nil
552  
553 newclass.instancemeta = {
554 __index = newclass.prototype,
555 __tostring = classobjectstring,
556 __eq = classobjectequal,
557 __lt = classobjectlessthan,
558 __le = classobjectlessthanequal,
559 __add = classobjectadd,
560 __sub = classobjectsub,
561 __unm = classobjectunm,
562 __mul = classobjectmul,
563 __div = classobjectdiv,
564 __pow = classobjectpow,
565 __concat = classobjectconcat,
566 }
567  
568 setmetatable(newclass, classmeta)
569  
570 newclass.new = class_new
571  
572 if newclass.mixins then
573 -- Fold in the mixins
574 local err, msg
575 for mixin in pairs(newclass.mixins) do
576 local ret
577 autoEmbed = true
578 ret, msg = pcall(mixin.embed, mixin, newclass.prototype)
579 autoEmbed = false
580 if not ret then
581 err = true
582 break
583 end
584 end
585  
586 if err then
587 local pt = newclass.prototype
588 for k,v in pairs(pt) do
589 pt[k] = nil
590 end
591  
592 -- method conflict
593 AceOO:error(msg)
594 end
595 end
596  
597 newclass.prototype.class = newclass
598  
599 if newclass.interfaces then
600 for interface in pairs(newclass.interfaces) do
601 traverseInterfaces(interface, newclass.interfaces)
602 end
603 end
604 if newclass.mixins then
605 for mixin in pairs(newclass.mixins) do
606 if mixin.interfaces then
607 if not newclass.interfaces then
608 newclass.interfaces = Compost and Compost:Acquire() or {}
609 end
610 for interface in pairs(mixin.interfaces) do
611 newclass.interfaces[interface] = true
612 end
613 end
614 end
615 end
616 end
617 function Class:ToString()
618 if type(self.GetLibraryVersion) == "function" then
619 return (self:GetLibraryVersion())
620 else
621 return "Class"
622 end
623 end
624  
625 local tmp
626 function Class.prototype:init()
627 if rawequal(self, initStatus) then
628 initStatus = nil
629 else
630 AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2)
631 end
632 self.uid = getuid(self)
633 local current = self.class
634 while true do
635 if current == Class then
636 break
637 end
638 if current.mixins then
639 for mixin in pairs(current.mixins) do
640 if type(mixin.OnInstanceInit) == "function" then
641 mixin:OnInstanceInit(self)
642 end
643 end
644 end
645 current = current.super
646 end
647 end
648 end
649  
650  
651 -- @object ClassFactory
652 -- @brief A factory for creating classes. Rarely used directly.
653 local ClassFactory = Factory(Object, Class, Object)
654  
655 function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
656 a12, a13, a14, a15, a16, a17, a18, a19, a20)
657 local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
658 a12, a13, a14, a15, a16, a17, a18, a19, a20)
659 if AceOO.classes then
660 AceOO.classes[x] = true
661 end
662 return x
663 end
664 getmetatable(Class).__call = Class.new
665  
666 -- @class Mixin
667 -- @brief A class to create mixin objects, which contain methods that get
668 -- "mixed in" to class prototypes.
669 --
670 -- @object Mixin prototype
671 -- @brief The prototype that mixin objects inherit their methods from.
672 --
673 -- @method Mixin prototype embed
674 -- @brief Mix in the methods of our object which are listed in our interface
675 -- to the supplied target table.
676 --
677 -- @method Mixin prototype init
678 -- @brief Initialize the mixin object.
679 -- @param newobj The new object we're initializing.
680 -- @param interface The interface we implement (the list of methods our
681 -- prototype provides which should be mixed into the target
682 -- table by embed).
683 do
684 Mixin = Class()
685 function Mixin:ToString()
686 if self.GetLibraryVersion then
687 return (self:GetLibraryVersion())
688 else
689 return 'Mixin'
690 end
691 end
692 local function _Embed(state, field, target)
693 field = next(state.export, field)
694 if field == nil then
695 return
696 end
697  
698 if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
699 AceOO:error("Method conflict in attempt to mixin. Field %q", field)
700 end
701  
702 target[field] = state[field]
703  
704 local ret,msg = pcall(_Embed, state, field, target)
705 if not ret then
706 -- Mix in the next method according to the defined interface. If that
707 -- fails due to a conflict, re-raise to back out the previous mixed
708 -- methods.
709  
710 target[field] = nil
711 AceOO:error(msg)
712 end
713 end
714 function Mixin.prototype:embed(target)
715 local mt = getmetatable(target)
716 setmetatable(target, nil)
717 local err, msg = pcall(_Embed, self, nil, target)
718 if not err then
719 setmetatable(target, mt)
720 AceOO:error(msg)
721 return
722 end
723 if type(self.embedList) == "table" then
724 self.embedList[target] = true
725 end
726 if type(target.class) ~= "table" then
727 target[self] = true
728 end
729 if not autoEmbed and type(self.OnManualEmbed) == "function" then
730 self:OnManualEmbed(target)
731 end
732 setmetatable(target, mt)
733 end
734  
735 function Mixin.prototype:activate(oldLib, oldDeactivate)
736 if oldLib and oldLib.embedList then
737 for target in pairs(oldLib.embedList) do
738 local mt = getmetatable(target)
739 setmetatable(target, nil)
740 for field in pairs(oldLib.export) do
741 target[field] = nil
742 end
743 setmetatable(target, mt)
744 end
745 self.embedList = oldLib.embedList
746 for target in pairs(self.embedList) do
747 self:embed(target)
748 end
749 else
750 self.embedList = setmetatable(Compost and Compost:Acquire() or {}, Compost and Compost:AcquireHash('__mode', 'k') or {__mode="k"})
751 end
752 end
753  
754 function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9,
755 a10, a11, a12, a13, a14, a15, a16,
756 a17, a18, a19, a20)
757 AceOO:argCheck(export, 2, "table")
758 for k,v in pairs(export) do
759 if type(k) ~= "number" then
760 AceOO:error("All keys to argument #2 must be numbers.")
761 elseif type(v) ~= "string" then
762 AceOO:error("All values to argument #2 must be strings.")
763 end
764 end
765 local num = table.getn(export)
766 for i = 1, num do
767 local v = export[i]
768 export[i] = nil
769 export[v] = true
770 end
771 table.setn(export, 0)
772 local interfaces
773 if a1 then
774 local l = getlibrary
775 interfaces = Compost and Compost:Acquire(l(a1), l(a2), l(a3), l(a4),
776 l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12),
777 l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20))
778 or { l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8),
779 l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16),
780 l(a17), l(a18), l(a19), l(a20) }
781 for _,v in ipairs(interfaces) do
782 if not v.class or not inherits(v, Interface) then
783 AceOO:error("Mixins can inherit only from interfaces")
784 end
785 end
786 local num = table.getn(interfaces)
787 for i = 1, num do
788 local v = interfaces[i]
789 interfaces[i] = nil
790 interfaces[v] = true
791 end
792 table.setn(interfaces, 0)
793 for interface in pairs(interfaces) do
794 traverseInterfaces(interface, interfaces)
795 end
796 for interface in pairs(interfaces) do
797 for field,kind in pairs(interface.interface) do
798 if kind ~= "nil" then
799 local good = false
800 for bit in pairs(export) do
801 if bit == field then
802 good = true
803 break
804 end
805 end
806 if not good then
807 AceOO:error("Mixin does not fully accommodate field %q", field)
808 end
809 end
810 end
811 end
812 end
813 self.super = Mixin.prototype
814 Mixin.super.prototype.init(self)
815 self.export = export
816 self.interfaces = interfaces
817 end
818 end
819  
820 -- @class Interface
821 -- @brief A class to create interfaces, which contain contracts that classes
822 -- which inherit from this must comply with.
823 --
824 -- @object Interface prototype
825 -- @brief The prototype that interface objects must adhere to.
826 --
827 -- @method Interface prototype init
828 -- @brief Initialize the mixin object.
829 -- @param interface The interface we contract (the hash of fields forced).
830 -- @param (a1..a20) Superinterfaces
831 do
832 Interface = Class()
833 function Interface:ToString()
834 if self.GetLibraryVersion then
835 return (self:GetLibraryVersion())
836 else
837 return 'Instance'
838 end
839 end
840 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)
841 Interface.super.prototype.init(self)
842 AceOO:argCheck(interface, 2, "table")
843 for k,v in pairs(interface) do
844 if type(k) ~= "string" then
845 AceOO:error("All keys to argument #2 must be numbers.")
846 elseif type(v) ~= "string" then
847 AceOO:error("All values to argument #2 must be strings.")
848 elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then
849 AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".')
850 end
851 end
852 local l = getlibrary
853 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)
854 if a1 then
855 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}
856 for k,v in ipairs(self.superinterfaces) do
857 if not inherits(v, Interface) or not v.class then
858 AceOO:error('Cannot provide a non-Interface to inherit from')
859 end
860 end
861 local num = table.getn(self.superinterfaces)
862 for i = 1, num do
863 local v = self.superinterfaces[i]
864 self.superinterfaces[i] = nil
865 self.superinterfaces[v] = true
866 end
867 table.setn(self.superinterfaces, 0)
868 end
869 self.interface = interface
870 end
871 end
872  
873 -- @function Classpool
874 -- @brief Obtain a read only class from our pool of classes, indexed by the
875 -- superclass and mixins.
876 -- @param sc The superclass of the class we want.
877 -- @param (m1..m20) Mixins of the class we want's objects.
878 -- @return A read only class from the class pool.
879 local Classpool
880 do
881 local pool = setmetatable({}, {__mode = 'v'})
882 local function newindex(k, v)
883 AceOO:error('Attempt to modify a read-only class.')
884 end
885 local function protonewindex(k, v)
886 AceOO:error('Attempt to modify a read-only class prototype.')
887 end
888 local function ts(bit)
889 if type(bit) ~= "table" then
890 return tostring(bit)
891 elseif getmetatable(bit) and bit.__tostring then
892 return tostring(bit)
893 elseif type(bit.GetLibraryVersion) == "function" then
894 return bit:GetLibraryVersion()
895 else
896 return tostring(bit)
897 end
898 end
899 local t
900 local function getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
901 m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
902 if not t then t = {} end
903 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
904 if m1 then if m1.uid then table.insert(t, m1.uid) else AceOO:error("%s is not an appropriate mixin", ts(m1)) end
905 if m2 then if m2.uid then table.insert(t, m2.uid) else AceOO:error("%s is not an appropriate mixin", ts(m2)) end
906 if m3 then if m3.uid then table.insert(t, m3.uid) else AceOO:error("%s is not an appropriate mixin", ts(m3)) end
907 if m4 then if m4.uid then table.insert(t, m4.uid) else AceOO:error("%s is not an appropriate mixin", ts(m4)) end
908 if m5 then if m5.uid then table.insert(t, m5.uid) else AceOO:error("%s is not an appropriate mixin", ts(m5)) end
909 if m6 then if m6.uid then table.insert(t, m6.uid) else AceOO:error("%s is not an appropriate mixin", ts(m6)) end
910 if m7 then if m7.uid then table.insert(t, m7.uid) else AceOO:error("%s is not an appropriate mixin", ts(m7)) end
911 if m8 then if m8.uid then table.insert(t, m8.uid) else AceOO:error("%s is not an appropriate mixin", ts(m8)) end
912 if m9 then if m9.uid then table.insert(t, m9.uid) else AceOO:error("%s is not an appropriate mixin", ts(m9)) end
913 if m10 then if m10.uid then table.insert(t, m10.uid) else AceOO:error("%s is not an appropriate mixin", ts(m10)) end
914 if m11 then if m11.uid then table.insert(t, m11.uid) else AceOO:error("%s is not an appropriate mixin", ts(m11)) end
915 if m12 then if m12.uid then table.insert(t, m12.uid) else AceOO:error("%s is not an appropriate mixin", ts(m12)) end
916 if m13 then if m13.uid then table.insert(t, m13.uid) else AceOO:error("%s is not an appropriate mixin", ts(m13)) end
917 if m14 then if m14.uid then table.insert(t, m14.uid) else AceOO:error("%s is not an appropriate mixin", ts(m14)) end
918 if m15 then if m15.uid then table.insert(t, m15.uid) else AceOO:error("%s is not an appropriate mixin", ts(m15)) end
919 if m16 then if m16.uid then table.insert(t, m16.uid) else AceOO:error("%s is not an appropriate mixin", ts(m16)) end
920 if m17 then if m17.uid then table.insert(t, m17.uid) else AceOO:error("%s is not an appropriate mixin", ts(m17)) end
921 if m18 then if m18.uid then table.insert(t, m18.uid) else AceOO:error("%s is not an appropriate mixin", ts(m18)) end
922 if m19 then if m19.uid then table.insert(t, m19.uid) else AceOO:error("%s is not an appropriate mixin", ts(m19)) end
923 if m20 then if m20.uid then table.insert(t, m20.uid) else AceOO:error("%s is not an appropriate mixin", ts(m20)) end
924 end end end end end end end end end end end end end end end end end end end end end
925 table.sort(t)
926 local uid = table.concat(t, '')
927 for k in pairs(t) do t[k] = nil end
928 table.setn(t, 0)
929 return uid
930 end
931 local classmeta
932 function Classpool(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
933 m10, m11, m12, m13, m14, m15, m16,
934 m17, m18, m19, m20)
935 local l = getlibrary
936 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)
937 if sc and sc.class then
938 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
939 end
940 sc = sc or Class
941 local key = getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
942 if not pool[key] then
943 local class = Class(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
944 m10, m11, m12, m13, m14, m15, m16, m17,
945 m18, m19, m20)
946 if not classmeta then
947 classmeta = {}
948 local mt = getmetatable(class)
949 for k,v in pairs(mt) do
950 classmeta[k] = v
951 end
952 classmeta.__newindex = newindex
953 end
954 -- Prevent the user from adding methods to this class.
955 -- NOTE: I'm not preventing modifications of existing class members,
956 -- but it's likely that only a truly malicious user will be doing so.
957 class.sealed = true
958 setmetatable(class, classmeta)
959 getmetatable(class.prototype).__newindex = protonewindex
960 pool[key] = class
961 end
962 return pool[key]
963 end
964 end
965  
966 AceOO.Factory = Factory
967 AceOO.Object = Object
968 AceOO.Class = Class
969 AceOO.Mixin = Mixin
970 AceOO.Interface = Interface
971 AceOO.Classpool = Classpool
972 AceOO.inherits = inherits
973  
974 -- Library handling bits
975  
976 local function activate(self, oldLib, oldDeactivate)
977 AceOO = self
978 Factory = self.Factory
979 Object = self.Object
980 Class = self.Class
981 ClassFactory.prototype = Class
982 Mixin = self.Mixin
983 Interface = self.Interface
984 Classpool = self.Classpool
985  
986 if oldLib then
987 self.classes = oldLib.classes
988 end
989 if not self.classes then
990 self.classes = setmetatable({}, {__mode="k"})
991 else
992 for class in pairs(self.classes) do
993 class.new = class_new
994 end
995 end
996  
997 if oldDeactivate then
998 oldDeactivate(oldLib)
999 end
1000 end
1001  
1002 local function external(self, major, instance)
1003 if major == "Compost-2.0" then
1004 Compost = instance
1005 end
1006 end
1007  
1008 AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
1009 AceOO = AceLibrary(MAJOR_VERSION)