1local middleclass = { 2 _VERSION = 'middleclass v4.0.0', 3 _DESCRIPTION = 'Object Orientation for Lua', 4 _URL = 'https://github.com/kikito/middleclass', 5 _LICENSE = [[ 6 MIT LICENSE 7 8 Copyright (c) 2011 Enrique García Cota 9 10 Permission is hereby granted, free of charge, to any person obtaining a 11 copy of this software and associated documentation files (the 12 "Software"), to deal in the Software without restriction, including 13 without limitation the rights to use, copy, modify, merge, publish, 14 distribute, sublicense, and/or sell copies of the Software, and to 15 permit persons to whom the Software is furnished to do so, subject to 16 the following conditions: 17 18 The above copyright notice and this permission notice shall be included 19 in all copies or substantial portions of the Software. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 ]] 29} 30 31local function _createIndexWrapper(aClass, f) 32 if f == nil then 33 return aClass.__instanceDict 34 else 35 return function(self, name) 36 local value = aClass.__instanceDict[name] 37 38 if value ~= nil then 39 return value 40 elseif type(f) == "function" then 41 return (f(self, name)) 42 else 43 return f[name] 44 end 45 end 46 end 47end 48 49local function _propagateInstanceMethod(aClass, name, f) 50 f = name == "__index" and _createIndexWrapper(aClass, f) or f 51 aClass.__instanceDict[name] = f 52 53 for subclass in pairs(aClass.subclasses) do 54 if rawget(subclass.__declaredMethods, name) == nil then 55 _propagateInstanceMethod(subclass, name, f) 56 end 57 end 58end 59 60local function _declareInstanceMethod(aClass, name, f) 61 aClass.__declaredMethods[name] = f 62 63 if f == nil and aClass.super then 64 f = aClass.super.__instanceDict[name] 65 end 66 67 _propagateInstanceMethod(aClass, name, f) 68end 69 70local function _tostring(self) return "class " .. self.name end 71local function _call(self, ...) return self:new(...) end 72 73local function _createClass(name, super) 74 local dict = {} 75 dict.__index = dict 76 77 local aClass = { name = name, super = super, static = {}, 78 __instanceDict = dict, __declaredMethods = {}, 79 subclasses = setmetatable({}, {__mode='k'}) } 80 81 if super then 82 setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or super.static[k] end }) 83 else 84 setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end }) 85 end 86 87 setmetatable(aClass, { __index = aClass.static, __tostring = _tostring, 88 __call = _call, __newindex = _declareInstanceMethod }) 89 90 return aClass 91end 92 93local function _includeMixin(aClass, mixin) 94 assert(type(mixin) == 'table', "mixin must be a table") 95 96 for name,method in pairs(mixin) do 97 if name ~= "included" and name ~= "static" then aClass[name] = method end 98 end 99 100 for name,method in pairs(mixin.static or {}) do 101 aClass.static[name] = method 102 end 103 104 if type(mixin.included)=="function" then mixin:included(aClass) end 105 return aClass 106end 107 108local DefaultMixin = { 109 __tostring = function(self) return "instance of " .. tostring(self.class) end, 110 111 initialize = function(self, ...) end, 112 113 isInstanceOf = function(self, aClass) 114 return type(self) == 'table' and 115 type(self.class) == 'table' and 116 type(aClass) == 'table' and 117 ( aClass == self.class or 118 type(aClass.isSubclassOf) == 'function' and 119 self.class:isSubclassOf(aClass) ) 120 end, 121 122 static = { 123 allocate = function(self) 124 assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'") 125 return setmetatable({ class = self }, self.__instanceDict) 126 end, 127 128 new = function(self, ...) 129 assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'") 130 local instance = self:allocate() 131 instance:initialize(...) 132 return instance 133 end, 134 135 subclass = function(self, name) 136 assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'") 137 assert(type(name) == "string", "You must provide a name(string) for your class") 138 139 local subclass = _createClass(name, self) 140 141 for methodName, f in pairs(self.__instanceDict) do 142 _propagateInstanceMethod(subclass, methodName, f) 143 end 144 subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end 145 146 self.subclasses[subclass] = true 147 self:subclassed(subclass) 148 149 return subclass 150 end, 151 152 subclassed = function(self, other) end, 153 154 isSubclassOf = function(self, other) 155 return type(other) == 'table' and 156 type(self) == 'table' and 157 type(self.super) == 'table' and 158 ( self.super == other or 159 type(self.super.isSubclassOf) == 'function' and 160 self.super:isSubclassOf(other) ) 161 end, 162 163 include = function(self, ...) 164 assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'") 165 for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end 166 return self 167 end 168 } 169} 170 171function middleclass.class(name, super) 172 assert(type(name) == 'string', "A name (string) is needed for the new class") 173 return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin) 174end 175 176setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end }) 177 178return middleclass 179