-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathobject.lua
286 lines (261 loc) · 8.95 KB
/
object.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
--==================================================================================================
-- Simple Object System
--==================================================================================================
local cache = {} -- The class cache
local iterating = false -- The class that SOS.iterate() is running on
local iterateCount = 0 -- Number of times iterated
local unnamed = 0 -- Total unnamed objects
local createQueue = {size=0} -- Objects created during iteration are queued
local deleteQueue = {size=0} -- Objects deleted during iteration are queued
--==================================================================================================
-- Base object
--==================================================================================================
local Object = {}
Object.__index = Object
Object._types = {Object=true}
Object._name = "Object"
Object._parents = {}
Object._children = {}
Object._instances = {}
Object._count = 0
Object._iterateCount = -1
Object._class = Object
cache["Object"] = Object
----------------------------------------------------------------------------------------------------
-- Creates an instance of an object
local classData
function Object:create(...)
local obj = setmetatable({}, self)
if iterating and self:typeOf(iterating) then
createQueue.size = createQueue.size + 1
createQueue[createQueue.size] = obj
else
self._instances[obj] = true
end
self._count = self._count + 1
obj._alive = true
if obj.onCreate then obj:onCreate(...) end
return obj
end
----------------------------------------------------------------------------------------------------
-- Destroy an instance
function Object:destroy(...)
if iterating and self:typeOf(iterating) then
deleteQueue.size = deleteQueue.size + 1
deleteQueue[deleteQueue.size] = self
else
self._instances[self] = nil
end
self._class._count = self._class._count - 1
self._alive = false
if self.onDestroy then self:onDestroy(...) end
end
----------------------------------------------------------------------------------------------------
-- Create a subclass of this object
function Object:subclass(className, ...)
return SOS.new(className, self, ...)
end
----------------------------------------------------------------------------------------------------
-- Iterate over all instances
function Object:iterate(excludeChildren)
return SOS.iterate(self._name, excludeChildren)
end
----------------------------------------------------------------------------------------------------
-- Tests to see if the object is of a certain type
function Object:typeOf(classType)
return self._types[classType]
end
----------------------------------------------------------------------------------------------------
-- Returns the class name of an object
function Object:getClassName()
return self._name
end
----------------------------------------------------------------------------------------------------
-- Returns true if the object has not been deleted
function Object:isAlive()
return self._alive
end
----------------------------------------------------------------------------------------------------
-- Calls a function on an object's parents
local parent
function Object:super(funct, ...)
for i = 1,#self._parents do
parent = self._parents[i]
if parent[funct] then parent[funct](parent, ...) end
end
end
--==================================================================================================
-- The interface
--==================================================================================================
local SOS = {}
----------------------------------------------------------------------------------------------------
-- Create a new class.
local tmp, obj, parents, parent
function SOS.new(className, ...)
if not className then
unnamed = unnamed + 1
className = "UnnamedObject" .. unnamed
end
className = tostring(className)
if cache[className] then
error( "SOS.new() - A class named " .. className .. " already exists." )
end
obj = {}
obj.__index = obj
obj._name = className
obj._parents = {}
obj._children = {}
obj._instances = {}
obj._types = {[className]=true}
obj._count = 0
obj._iterateCount = -1
obj._class = obj
cache[className] = obj
parents = {...}
if #parents <= 0 then parents[1] = Object end
for i = 1, #parents do
parent = cache[parents[i]] or parents[i]
for k, v in pairs(parent) do
if not obj[k] then obj[k] = v end
end
for classType, _ in pairs(parent._types) do
obj._types[classType] = true
end
tmp = cache[className]._parents
tmp[#tmp+1] = parent
tmp = cache[parent._name]._children
tmp[#tmp+1] = obj
end
return setmetatable(obj, self)
end
----------------------------------------------------------------------------------------------------
-- Iterate over all all instances of a certain type. Order is undefined.
local c, returnValue
function SOS.iterate(class, excludeChildren)
class = class or "Object"
if type(class) ~= "string" then class = class._name end
if iterating then error("SOS.iterate() - Can't perform nested iteration") end
iterating = class
iterateCount = iterateCount + 1
c = cache[class] -- class iterator
c._i = next(c._instances) -- instance iterator
c._c = 1 -- children iterator
c._up = nil -- return parent
return function()
while true do
if c._instances[c._i] and c._iterateCount ~= iterateCount then
returnValue = c._i
c._i = next(c._instances, c._i)
return returnValue
end
c._iterateCount = iterateCount
if c._children[c._c] and not excludeChildren then
c._children[c._c]._up = c
c = c._children[c._c]
c._up._c = c._up._c + 1
c._i = next(c._instances)
c._c = 1
elseif c._up then
c = c._up
else
if createQueue.size > 0 then
for i = 1, createQueue.size do
createQueue[i]._instances[createQueue[i]] = true
end
createQueue.size = 0
end
if deleteQueue.size > 0 then
for i = 1, deleteQueue.size do
deleteQueue[i]._instances[deleteQueue[i]] = nil
end
deleteQueue.size = 0
end
break
end
end
iterating = false
return nil
end
end
----------------------------------------------------------------------------------------------------
-- Returns a defined class
function SOS.get(className)
return cache[className]
end
SOS.__call = SOS.get
setmetatable(SOS, SOS)
--==================================================================================================
-- Debugging tools (Some of these are slow)
--==================================================================================================
----------------------------------------------------------------------------------------------------
-- Returns the children of a certain class
function SOS.children(class)
class = cache[class] or class
return { unpack(class.children) }
end
----------------------------------------------------------------------------------------------------
-- Returns the parents of a certain class
function SOS.parents(class)
class = cache[class] or class
return { unpack(class.parents) }
end
----------------------------------------------------------------------------------------------------
-- Returns all of the ancestors of a certain class
function SOS.ancestors(class)
class = cache[class] or class
local ancestors = {}
for className,_ in pairs(class._types) do
if className ~= class._name then
ancestors[ #ancestors+1 ] = cache[className]
end
end
return ancestors
end
----------------------------------------------------------------------------------------------------
-- Counts the number of a certain object, including children.
function SOS.count(obj)
obj = obj or "Object"
if type(obj) == "table" then obj = obj._name end
local count = 0
for _, classData in pairs(cache) do
if classData._types[obj] then
count = count + classData.count
end
end
return count
end
----------------------------------------------------------------------------------------------------
-- Returns a string representing the class structure and object count
local encountered, count
function SOS.info()
count = {}
encountered = {}
for _, class in pairs(cache) do
for name, _ in pairs(class._types) do
if not count[name] then count[name] = 0 end
count[name] = count[name] + class._count
end
end
return "Class (Instances:Children) \n" .. structure(cache.Object, "")
end
function structure(obj, indent)
indent = indent .. ">"
local txt = string.format("%s %s (%s:%s)\n", indent, obj._name, "%d", "%d")
txt = string.format(txt, obj._count, count[obj._name] - obj._count)
local previous = {}
for k,child in pairs(obj._children) do
if not encountered[child] then
encountered[child] = true
txt = txt .. structure(child, indent)
else
child = child._name
previous[ #previous+1 ] = string.format("%s(%d)", child, count[child])
end
end
if #previous > 0 then
txt = txt .. string.format("%s> MIXED: %s\n", indent, table.concat(previous, ", "))
end
return txt
end
----------------------------------------------------------------------------------------------------
return SOS