312 lines
9.8 KiB
Lua
Executable File
312 lines
9.8 KiB
Lua
Executable File
--------------------------------------------------------------------------------
|
|
---------------------- ## ##### ##### ###### -----------------------
|
|
---------------------- ## ## ## ## ## ## ## -----------------------
|
|
---------------------- ## ## ## ## ## ###### -----------------------
|
|
---------------------- ## ## ## ## ## ## -----------------------
|
|
---------------------- ###### ##### ##### ## -----------------------
|
|
---------------------- -----------------------
|
|
----------------------- Lua Object-Oriented Programming ------------------------
|
|
--------------------------------------------------------------------------------
|
|
-- Project: LOOP - Lua Object-Oriented Programming --
|
|
-- Release: 2.3 beta --
|
|
-- Title : Cached Class Model --
|
|
-- Author : Renato Maia <maia@inf.puc-rio.br> --
|
|
--------------------------------------------------------------------------------
|
|
-- Exported API: --
|
|
-- class(class, ...) --
|
|
-- new(class, ...) --
|
|
-- classof(object) --
|
|
-- isclass(class) --
|
|
-- instanceof(object, class) --
|
|
-- memberof(class, name) --
|
|
-- members(class) --
|
|
-- superclass(class) --
|
|
-- subclassof(class, super) --
|
|
-- supers(class) --
|
|
-- allmembers(class) --
|
|
--------------------------------------------------------------------------------
|
|
|
|
local type = type
|
|
local unpack = unpack
|
|
local pairs = pairs
|
|
local rawget = rawget
|
|
local rawset = rawset
|
|
local require = require
|
|
local ipairs = ipairs
|
|
local setmetatable = setmetatable
|
|
local select = select
|
|
|
|
local table = require "loop.table"
|
|
|
|
module "loop.cached"
|
|
--------------------------------------------------------------------------------
|
|
local OrderedSet = require "loop.collection.OrderedSet"
|
|
local base = require "loop.multiple"
|
|
--------------------------------------------------------------------------------
|
|
table.copy(base, _M)
|
|
--------------------------------------------------------------------------------
|
|
local function subsiterator(queue, class)
|
|
class = queue[class]
|
|
if class then
|
|
for sub in pairs(class.subs) do
|
|
queue:enqueue(sub)
|
|
end
|
|
return class
|
|
end
|
|
end
|
|
function subs(class)
|
|
queue = OrderedSet()
|
|
queue:enqueue(class)
|
|
return subsiterator, queue, OrderedSet.firstkey
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
local function proxy_newindex(proxy, field, value)
|
|
return base.classof(proxy):updatefield(field, value)
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function getclass(class)
|
|
local cached = base.classof(class)
|
|
if base.instanceof(cached, CachedClass) then
|
|
return cached
|
|
end
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
local ClassMap = base.new { __mode = "k" }
|
|
--------------------------------------------------------------------------------
|
|
CachedClass = base.class()
|
|
|
|
function CachedClass:__init(class)
|
|
local meta = {}
|
|
self = base.rawnew(self, {
|
|
__call = new,
|
|
__index = meta,
|
|
__newindex = proxy_newindex,
|
|
supers = {},
|
|
subs = {},
|
|
members = table.copy(class, {}),
|
|
class = meta,
|
|
})
|
|
self.proxy = setmetatable(class and table.clear(class) or {}, self)
|
|
ClassMap[self.class] = self.proxy
|
|
return self
|
|
end
|
|
|
|
function CachedClass:updatehierarchy(...)
|
|
-- separate cached from non-cached classes
|
|
local caches = {}
|
|
local supers = {}
|
|
for i = 1, select("#", ...) do
|
|
local super = select(i, ...)
|
|
local cached = getclass(super)
|
|
if cached
|
|
then caches[#caches + 1] = cached
|
|
else supers[#supers + 1] = super
|
|
end
|
|
end
|
|
|
|
-- remove it from its old superclasses
|
|
for _, super in ipairs(self.supers) do
|
|
super:removesubclass(self)
|
|
end
|
|
|
|
-- update superclasses
|
|
self.uncached = supers
|
|
self.supers = caches
|
|
|
|
-- register as subclass in all superclasses
|
|
for _, super in ipairs(self.supers) do
|
|
super:addsubclass(self)
|
|
end
|
|
end
|
|
|
|
function CachedClass:updateinheritance()
|
|
-- relink all affected classes
|
|
for sub in subs(self) do
|
|
sub:updatemembers()
|
|
sub:updatesuperclasses()
|
|
end
|
|
end
|
|
|
|
function CachedClass:addsubclass(class)
|
|
self.subs[class] = true
|
|
end
|
|
|
|
function CachedClass:removesubclass(class)
|
|
self.subs[class] = nil
|
|
end
|
|
|
|
function CachedClass:updatesuperclasses()
|
|
local uncached = {}
|
|
-- copy uncached superclasses defined in the class
|
|
for _, super in ipairs(self.uncached) do
|
|
if not uncached[super] then
|
|
uncached[super] = true
|
|
uncached[#uncached + 1] = super
|
|
end
|
|
end
|
|
-- copy inherited uncached superclasses
|
|
for _, cached in ipairs(self.supers) do
|
|
for _, super in base.supers(cached.class) do
|
|
if not uncached[super] then
|
|
uncached[super] = true
|
|
uncached[#uncached + 1] = super
|
|
end
|
|
end
|
|
end
|
|
base.class(self.class, unpack(uncached))
|
|
end
|
|
|
|
function CachedClass:updatemembers()
|
|
local class = table.clear(self.class)
|
|
for i = #self.supers, 1, -1 do
|
|
local super = self.supers[i].class
|
|
-- copy inherited members
|
|
table.copy(super, class)
|
|
-- do not copy the default __index value
|
|
if rawget(class, "__index") == super then
|
|
rawset(class, "__index", nil)
|
|
end
|
|
end
|
|
-- copy members defined in the class
|
|
table.copy(self.members, class)
|
|
-- set the default __index value
|
|
if rawget(class, "__index") == nil then
|
|
rawset(class, "__index", class)
|
|
end
|
|
end
|
|
|
|
function CachedClass:updatefield(name, member)
|
|
-- update member list
|
|
local members = self.members
|
|
members[name] = member
|
|
|
|
-- get old linkage
|
|
local class = self.class
|
|
local old = class[name]
|
|
|
|
-- replace old linkage for the new one
|
|
class[name] = member
|
|
local queue = OrderedSet()
|
|
for sub in pairs(self.subs) do
|
|
queue:enqueue(sub)
|
|
end
|
|
while queue:head() do
|
|
local current = queue:dequeue()
|
|
class = current.class
|
|
members = current.members
|
|
if members[name] == nil then
|
|
for _, super in ipairs(current.supers) do
|
|
local superclass = super.class
|
|
if superclass[name] ~= nil then
|
|
if superclass[name] ~= class[name] then
|
|
class[name] = superclass[name]
|
|
for sub in pairs(current.subs) do
|
|
queue:enqueue(sub)
|
|
end
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return old
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function class(class, ...)
|
|
class = getclass(class) or CachedClass(class)
|
|
class:updatehierarchy(...)
|
|
class:updateinheritance()
|
|
return class.proxy
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function rawnew(class, object)
|
|
local cached = getclass(class)
|
|
if cached then class = cached.class end
|
|
return base.rawnew(class, object)
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function new(class, ...)
|
|
if class.__init
|
|
then return class:__init(...)
|
|
else return rawnew(class, ...)
|
|
end
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function classof(object)
|
|
local class = base.classof(object)
|
|
return ClassMap[class] or class
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function isclass(class)
|
|
return getclass(class) ~= nil
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function superclass(class)
|
|
local supers = {}
|
|
local cached = getclass(class)
|
|
if cached then
|
|
for index, super in ipairs(cached.supers) do
|
|
supers[index] = super.proxy
|
|
end
|
|
class = cached.class
|
|
end
|
|
for _, super in base.supers(class) do
|
|
supers[#supers + 1] = super
|
|
end
|
|
return unpack(supers)
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
local function icached(cached, index)
|
|
local super
|
|
local supers = cached.supers
|
|
index = index + 1
|
|
-- check if index points to a cached superclass
|
|
super = supers[index]
|
|
if super then return index, super.proxy end
|
|
-- check if index points to an uncached superclass
|
|
super = cached.uncached[index - #supers]
|
|
if super then return index, super end
|
|
end
|
|
function supers(class)
|
|
local cached = getclass(class)
|
|
if cached
|
|
then return icached, cached, 0
|
|
else return base.supers(class)
|
|
end
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function subclassof(class, super)
|
|
if class == super then return true end
|
|
for _, superclass in supers(class) do
|
|
if subclassof(superclass, super) then return true end
|
|
end
|
|
return false
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function instanceof(object, class)
|
|
return subclassof(classof(object), class)
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function memberof(class, name)
|
|
local cached = getclass(class)
|
|
if cached
|
|
then return cached.members[name]
|
|
else return base.member(class, name)
|
|
end
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function members(class)
|
|
local cached = getclass(class)
|
|
if cached
|
|
then return pairs(cached.members)
|
|
else return base.members(class)
|
|
end
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
function allmembers(class)
|
|
local cached = getclass(class)
|
|
if cached
|
|
then return pairs(cached.class)
|
|
else return base.members(class)
|
|
end
|
|
end |