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: 11577 $
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
5 Inspired By: Ace 1.x by Turan (turan@gryphon.com)
6 Website: http://www.wowace.com/
7 Documentation: http://www.wowace.com/index.php/AceOO-2.0
8 SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
9 Description: Library to provide an object-orientation framework.
10 Dependencies: AceLibrary
11 ]]
12  
13 local MAJOR_VERSION = "AceOO-2.0"
14 local MINOR_VERSION = "$Revision: 11577 $"
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 table_setn
21 do
22 local version = GetBuildInfo()
23 if string.find(version, "^2%.") then
24 -- 2.0.0
25 table_setn = function() end
26 else
27 table_setn = table.setn
28 end
29 end
30  
31 local AceOO = {
32 error = AceLibrary.error,
33 argCheck = AceLibrary.argCheck
34 }
35  
36 -- @function getuid
37 -- @brief Obtain a unique string identifier for the object in question.
38 -- @param t The object to obtain the uid for.
39 -- @return The uid string.
40 local function pad(cap)
41 return string.rep('0', 8 - string.len(cap)) .. cap
42 end
43 local function getuid(t)
44 local mt = getmetatable(t)
45 setmetatable(t, nil)
46 local str = tostring(t)
47 setmetatable(t, mt)
48 local _,_,cap = string.find(str, '[^:]*: 0x(.*)$')
49 if cap then return pad(cap) end
50 _,_,cap = string.find(str, '[^:]*: (.*)$')
51 if cap then return pad(cap) end
52 end
53  
54 local function getlibrary(o)
55 if type(o) == "table" then
56 return o
57 elseif type(o) == "string" then
58 if not AceLibrary:HasInstance(o) then
59 AceOO:error("Library %q does not exist.", o)
60 end
61 return AceLibrary(o)
62 end
63 end
64  
65 -- @function Factory
66 -- @brief Construct a factory for the creation of objects.
67 -- @param obj The object whose init method will be called on the new factory
68 -- object.
69 -- @param newobj The object whose init method will be called on the new
70 -- objects that the Factory creates, to initialize them.
71 -- @param (a1..a20) Arguments which will be passed to obj.init() in addition
72 -- to the Factory object.
73 -- @return The new factory which creates a newobj when its new method is called,
74 -- or when it is called directly (__call metamethod).
75 local Factory
76 do
77 local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
78 a13, a14, a15, a16, a17, a18, a19, a20)
79 local t = {}
80 local uid = getuid(t)
81 local l = getlibrary
82 obj:init(t, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
83 l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
84 l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
85 l(a20))
86 t.uid = uid
87 return t
88 end
89  
90 local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
91 a11, a12, a13, a14, a15, a16, a17, a18,
92 a19, a20)
93 local o = self.prototype
94 local l = getlibrary
95 return new(o, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
96 l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
97 l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
98 l(a20))
99 end
100  
101 function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
102 a11, a12, a13, a14, a15, a16, a17, a18,
103 a19, a20)
104 local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
105 a13, a14, a15, a16, a17, a18, a19, a20)
106 t.prototype = newobj
107 t.new = createnew
108 getmetatable(t).__call = t.new
109 return t
110 end
111 end
112  
113  
114 local function objtostring(self)
115 if self.ToString then
116 return self:ToString()
117 elseif self.GetLibraryVersion then
118 return (self:GetLibraryVersion())
119 elseif self.super then
120 local s = "Sub-" .. tostring(self.super)
121 local first = true
122 if self.interfaces then
123 for interface in pairs(self.interfaces) do
124 if first then
125 s = s .. "(" .. tostring(interface)
126 first = false
127 else
128 s = s .. ", " .. tostring(interface)
129 end
130 end
131 end
132 if self.mixins then
133 for mixin in pairs(self.mixins) do
134 if first then
135 s = s .. tostring(mixin)
136 first = false
137 else
138 s = s .. ", " .. tostring(mixin)
139 end
140 end
141 end
142 if first then
143 if self.uid then
144 return s .. ":" .. self.uid
145 else
146 return s
147 end
148 else
149 return s .. ")"
150 end
151 else
152 return self.uid and 'Subclass:' .. self.uid or 'Subclass'
153 end
154 end
155  
156 -- @table Object
157 -- @brief Base of all objects, including Class.
158 --
159 -- @method init
160 -- @brief Initialize a new object.
161 -- @param newobject The object to initialize
162 -- @param class The class to make newobject inherit from
163 local Object
164 do
165 Object = {}
166 function Object:init(newobject, class)
167 local parent = class or self
168 if not rawget(newobject, 'uid') then
169 newobject.uid = getuid(newobject)
170 end
171 local mt = {
172 __index = parent,
173 __tostring = objtostring,
174 }
175 setmetatable(newobject, mt)
176 end
177 Object.uid = getuid(Object)
178 setmetatable(Object, { __tostring = function() return 'Object' end })
179 end
180  
181 local Interface
182  
183 local function validateInterface(object, interface)
184 if not object.class and object.prototype then
185 object = object.prototype
186 end
187 for k,v in pairs(interface.interface) do
188 if tostring(type(object[k])) ~= v then
189 return false
190 end
191 end
192 if interface.superinterfaces then
193 for superinterface in pairs(interface.superinterfaces) do
194 if not validateInterface(object, superinterface) then
195 return false
196 end
197 end
198 end
199 if type(object.class) == "table" and rawequal(object.class.prototype, object) then
200 if not object.class.interfaces then
201 rawset(object.class, 'interfaces', {})
202 end
203 object.class.interfaces[interface] = true
204 elseif type(object.class) == "table" and type(object.class.prototype) == "table" then
205 validateInterface(object.class.prototype, interface)
206 -- check if class is proper, thus preventing future checks.
207 end
208 return true
209 end
210  
211 -- @function inherits
212 -- @brief Return whether an Object or Class inherits from a given
213 -- parent.
214 -- @param object Object or Class to check
215 -- @param parent Parent to test inheritance from
216 -- @return whether an Object or Class inherits from a given
217 -- parent.
218 local function inherits(object, parent)
219 object = getlibrary(object)
220 if type(parent) == "string" then
221 if not AceLibrary:HasInstance(parent) then
222 return false
223 else
224 parent = AceLibrary(parent)
225 end
226 end
227 AceOO:argCheck(parent, 2, "table")
228 if type(object) ~= "table" then
229 return false
230 end
231 local current
232 if object.class then
233 current = object.class
234 else
235 current = object
236 end
237 if type(current) ~= "table" then
238 return false
239 end
240 if rawequal(current, parent) then
241 return true
242 end
243 if parent.class then
244 while true do
245 if rawequal(current, Object) then
246 break
247 end
248 if current.mixins then
249 for mixin in pairs(current.mixins) do
250 if rawequal(mixin, parent) then
251 return true
252 end
253 end
254 end
255 if current.interfaces then
256 for interface in pairs(current.interfaces) do
257 if rawequal(interface, parent) then
258 return true
259 end
260 end
261 end
262 current = current.super
263 if type(current) ~= "table" then
264 break
265 end
266 end
267  
268 local isInterface = false
269 local curr = parent.class
270 while true do
271 if rawequal(curr, Object) then
272 break
273 elseif rawequal(curr, Interface) then
274 isInterface = true
275 break
276 end
277 curr = curr.super
278 if type(curr) ~= "table" then
279 break
280 end
281 end
282 return isInterface and validateInterface(object, parent)
283 else
284 while true do
285 if rawequal(current, parent) then
286 return true
287 elseif rawequal(current, Object) then
288 return false
289 end
290 current = current.super
291 if type(current) ~= "table" then
292 return false
293 end
294 end
295 end
296 end
297  
298 -- @table Class
299 -- @brief An object factory which sets up inheritence and supports
300 -- 'mixins'.
301 --
302 -- @metamethod Class call
303 -- @brief Call ClassFactory:new() to create a new class.
304 --
305 -- @method Class new
306 -- @brief Construct a new object.
307 -- @param (a1..a20) Arguments to pass to the object init function.
308 -- @return The new object.
309 --
310 -- @method Class init
311 -- @brief Initialize a new class.
312 -- @param parent Superclass.
313 -- @param (a1..a20) Mixins.
314 --
315 -- @method Class ToString
316 -- @return A string representing the object, in this case 'Class'.
317 local initStatus
318 local Class
319 local Mixin
320 local autoEmbed = false
321 local function traverseInterfaces(bit, total)
322 if bit.superinterfaces then
323 for interface in pairs(bit.superinterfaces) do
324 if not total[interface] then
325 total[interface] = true
326 traverseInterfaces(interface, total)
327 end
328 end
329 end
330 end
331 local class_new
332 do
333 Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
334 Class.super = Object
335  
336 local function protostring(t)
337 return '<' .. tostring(t.class) .. ' prototype>'
338 end
339 local function classobjectstring(t)
340 if t.ToString then
341 return t:ToString()
342 elseif t.GetLibraryVersion then
343 return (t:GetLibraryVersion())
344 else
345 return '<' .. tostring(t.class) .. ' instance>'
346 end
347 end
348 local function classobjectequal(self, other)
349 if type(self) == "table" and self.Equals then
350 return self:Equals(other)
351 elseif type(other) == "table" and other.Equals then
352 return other:Equals(self)
353 elseif type(self) == "table" and self.CompareTo then
354 return self:CompareTo(other) == 0
355 elseif type(other) == "table" and other.CompareTo then
356 return other:CompareTo(self) == 0
357 else
358 return rawequal(self, other)
359 end
360 end
361 local function classobjectlessthan(self, other)
362 if type(self) == "table" and self.IsLessThan then
363 return self:IsLessThan(other)
364 elseif type(other) == "table" and other.IsLessThanOrEqualTo then
365 return not other:IsLessThanOrEqualTo(self)
366 elseif type(self) == "table" and self.CompareTo then
367 return self:CompareTo(other) < 0
368 elseif type(other) == "table" and other.CompareTo then
369 return other:CompareTo(self) > 0
370 elseif type(other) == "table" and other.IsLessThan and other.Equals then
371 return other:Equals(self) or other:IsLessThan(self)
372 else
373 AceOO:error("cannot compare two objects")
374 end
375 end
376 local function classobjectlessthanequal(self, other)
377 if type(self) == "table" and self.IsLessThanOrEqualTo then
378 return self:IsLessThanOrEqualTo(other)
379 elseif type(other) == "table" and other.IsLessThan then
380 return not other:IsLessThan(self)
381 elseif type(self) == "table" and self.CompareTo then
382 return self:CompareTo(other) <= 0
383 elseif type(other) == "table" and other.CompareTo then
384 return other:CompareTo(self) >= 0
385 elseif type(self) == "table" and self.IsLessThan and self.Equals then
386 return self:Equals(other) or self:IsLessThan(other)
387 else
388 AceOO:error("cannot compare two incompatible objects")
389 end
390 end
391 local function classobjectadd(self, other)
392 if type(self) == "table" and self.Add then
393 return self:Add(other)
394 else
395 AceOO:error("cannot add two incompatible objects")
396 end
397 end
398 local function classobjectsub(self, other)
399 if type(self) == "table" and self.Subtract then
400 return self:Subtract(other)
401 else
402 AceOO:error("cannot subtract two incompatible objects")
403 end
404 end
405 local function classobjectunm(self, other)
406 if type(self) == "table" and self.UnaryNegation then
407 return self:UnaryNegation(other)
408 else
409 AceOO:error("attempt to negate an incompatible object")
410 end
411 end
412 local function classobjectmul(self, other)
413 if type(self) == "table" and self.Multiply then
414 return self:Multiply(other)
415 else
416 AceOO:error("cannot multiply two incompatible objects")
417 end
418 end
419 local function classobjectdiv(self, other)
420 if type(self) == "table" and self.Divide then
421 return self:Divide(other)
422 else
423 AceOO:error("cannot divide two incompatible objects")
424 end
425 end
426 local function classobjectpow(self, other)
427 if type(self) == "table" and self.Exponent then
428 return self:Exponent(other)
429 else
430 AceOO:error("cannot exponentiate two incompatible objects")
431 end
432 end
433 local function classobjectconcat(self, other)
434 if type(self) == "table" and self.Concatenate then
435 return self:Concatenate(other)
436 else
437 AceOO:error("cannot concatenate two incompatible objects")
438 end
439 end
440 function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
441 a13, a14, a15, a16, a17, a18, a19, a20)
442 if self.virtual then
443 AceOO:error("Cannot instantiate a virtual class.")
444 end
445  
446 local o = self.prototype
447 local newobj = {}
448 if o.class and o.class.instancemeta then
449 setmetatable(newobj, o.class.instancemeta)
450 else
451 Object:init(newobj, o)
452 end
453  
454 if self.interfaces and not self.interfacesVerified then
455 -- Verify the interfaces
456  
457 for interface in pairs(self.interfaces) do
458 for field,kind in pairs(interface.interface) do
459 if tostring(type(newobj[field])) ~= kind then
460 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])))
461 end
462 end
463 end
464 self.interfacesVerified = true
465 end
466 local tmp = initStatus
467 initStatus = newobj
468 newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
469 a13, a14, a15, a16, a17, a18, a19, a20)
470 if initStatus then
471 initStatus = tmp
472 AceOO:error("Initialization not completed, be sure to call the superclass's init method.")
473 return
474 end
475 initStatus = tmp
476 return newobj
477 end
478 local classmeta = {
479 __tostring = objtostring,
480 __call = function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
481 a13, a14, a15, a16, a17, a18, a19, a20)
482 return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
483 a13, a14, a15, a16, a17, a18, a19, a20)
484 end,
485 }
486 function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
487 a10, a11, a12, a13, a14, a15, a16,
488 a17, a18, a19, a20)
489 parent = parent or self
490  
491 local total
492  
493 if parent.class then
494 total = {
495 parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
496 a10, a11, a12, a13, a14, a15, a16,
497 a17, a18, a19, a20
498 }
499 parent = self
500 else
501 total = {
502 a1, a2, a3, a4, a5, a6, a7, a8, a9,
503 a10, a11, a12, a13, a14, a15, a16,
504 a17, a18, a19, a20
505 }
506 end
507 if not inherits(parent, Class) then
508 AceOO:error("Classes must inherit from a proper class")
509 end
510 if parent.sealed then
511 AceOO:error("Cannot inherit from a sealed class")
512 end
513 for i,v in ipairs(total) do
514 if inherits(v, Mixin) and v.class then
515 if not newclass.mixins then
516 newclass.mixins = {}
517 end
518 if newclass.mixins[v] then
519 AceOO:error("Cannot explicitly inherit from the same mixin twice")
520 end
521 newclass.mixins[v] = true
522 elseif inherits(v, Interface) and v.class then
523 if not newclass.interfaces then
524 newclass.interfaces = {}
525 end
526 if newclass.interfaces[v] then
527 AceOO:error("Cannot explicitly inherit from the same interface twice")
528 end
529 newclass.interfaces[v] = true
530 else
531 AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
532 end
533 end
534 if parent.interfaces then
535 if newclass.interfaces then
536 for interface in pairs(parent.interfaces) do
537 newclass.interfaces[interface] = true
538 end
539 else
540 newclass.interfaces = parent.interfaces
541 end
542 end
543 for k in pairs(total) do
544 total[k] = nil
545 end
546 table_setn(total, 0)
547  
548 newclass.super = parent
549  
550 newclass.prototype = setmetatable(total, {
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 = {}
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 local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
661 a12, a13, a14, a15, a16, a17, a18, a19, a20)
662 if AceOO.classes then
663 AceOO.classes[x] = true
664 end
665 return x
666 end
667 getmetatable(Class).__call = Class.new
668  
669 -- @class Mixin
670 -- @brief A class to create mixin objects, which contain methods that get
671 -- "mixed in" to class prototypes.
672 --
673 -- @object Mixin prototype
674 -- @brief The prototype that mixin objects inherit their methods from.
675 --
676 -- @method Mixin prototype embed
677 -- @brief Mix in the methods of our object which are listed in our interface
678 -- to the supplied target table.
679 --
680 -- @method Mixin prototype init
681 -- @brief Initialize the mixin object.
682 -- @param newobj The new object we're initializing.
683 -- @param interface The interface we implement (the list of methods our
684 -- prototype provides which should be mixed into the target
685 -- table by embed).
686 do
687 Mixin = Class()
688 function Mixin:ToString()
689 if self.GetLibraryVersion then
690 return (self:GetLibraryVersion())
691 else
692 return 'Mixin'
693 end
694 end
695 local function _Embed(state, field, target)
696 field = next(state.export, field)
697 if field == nil then
698 return
699 end
700  
701 if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
702 AceOO:error("Method conflict in attempt to mixin. Field %q", field)
703 end
704  
705 target[field] = state[field]
706  
707 local ret,msg = pcall(_Embed, state, field, target)
708 if not ret then
709 -- Mix in the next method according to the defined interface. If that
710 -- fails due to a conflict, re-raise to back out the previous mixed
711 -- methods.
712  
713 target[field] = nil
714 AceOO:error(msg)
715 end
716 end
717 function Mixin.prototype:embed(target)
718 local mt = getmetatable(target)
719 setmetatable(target, nil)
720 local err, msg = pcall(_Embed, self, nil, target)
721 if not err then
722 setmetatable(target, mt)
723 AceOO:error(msg)
724 return
725 end
726 if type(self.embedList) == "table" then
727 self.embedList[target] = true
728 end
729 if type(target.class) ~= "table" then
730 target[self] = true
731 end
732 if not autoEmbed and type(self.OnManualEmbed) == "function" then
733 self:OnManualEmbed(target)
734 end
735 setmetatable(target, mt)
736 end
737  
738 function Mixin.prototype:activate(oldLib, oldDeactivate)
739 if oldLib and oldLib.embedList then
740 for target in pairs(oldLib.embedList) do
741 local mt = getmetatable(target)
742 setmetatable(target, nil)
743 for field in pairs(oldLib.export) do
744 target[field] = nil
745 end
746 setmetatable(target, mt)
747 end
748 self.embedList = oldLib.embedList
749 for target in pairs(self.embedList) do
750 self:embed(target)
751 end
752 else
753 self.embedList = setmetatable({}, {__mode="k"})
754 end
755 end
756  
757 function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9,
758 a10, a11, a12, a13, a14, a15, a16,
759 a17, a18, a19, a20)
760 AceOO:argCheck(export, 2, "table")
761 for k,v in pairs(export) do
762 if type(k) ~= "number" then
763 AceOO:error("All keys to argument #2 must be numbers.")
764 elseif type(v) ~= "string" then
765 AceOO:error("All values to argument #2 must be strings.")
766 end
767 end
768 local num = table.getn(export)
769 for i = 1, num do
770 local v = export[i]
771 export[i] = nil
772 export[v] = true
773 end
774 table_setn(export, 0)
775 local interfaces
776 if a1 then
777 local l = getlibrary
778 interfaces = { 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 = {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 AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate)
1003 AceOO = AceLibrary(MAJOR_VERSION)