355 lines
10 KiB
Lua
Executable File
355 lines
10 KiB
Lua
Executable File
--------------------------------------------------------------------------------
|
|
---------------------- ## ##### ##### ###### -----------------------
|
|
---------------------- ## ## ## ## ## ## ## -----------------------
|
|
---------------------- ## ## ## ## ## ###### -----------------------
|
|
---------------------- ## ## ## ## ## ## -----------------------
|
|
---------------------- ###### ##### ##### ## -----------------------
|
|
---------------------- -----------------------
|
|
----------------------- Lua Object-Oriented Programming ------------------------
|
|
--------------------------------------------------------------------------------
|
|
-- Project: LOOP - Lua Object-Oriented Programming --
|
|
-- Release: 2.3 beta --
|
|
-- Title : Port Model with Interception Support --
|
|
-- Author : Renato Maia <maia@inf.puc-rio.br> --
|
|
--------------------------------------------------------------------------------
|
|
-- Exported API: --
|
|
-- Facet --
|
|
-- Receptacle --
|
|
-- ListReceptacle --
|
|
-- HashReceptacle --
|
|
-- SetReceptacle --
|
|
-- intercept(template|factory|comp, portname, event, interceptor) --
|
|
--------------------------------------------------------------------------------
|
|
|
|
local getmetatable = getmetatable
|
|
local pairs = pairs
|
|
local rawget = rawget
|
|
local rawset = rawset
|
|
local tostring = tostring
|
|
local type = type
|
|
|
|
local ObjectCache = require "loop.collection.ObjectCache"
|
|
local oo = require "loop.cached"
|
|
local base = require "loop.component.base"
|
|
|
|
module "loop.component.intercepted"
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
local function doafter(iceptor, request, method, ...)
|
|
local operation = iceptor.after
|
|
if operation then
|
|
if request.cancel
|
|
then return operation(iceptor, request, ...)
|
|
else return operation(iceptor, request, method(...))
|
|
end
|
|
else
|
|
if request.cancel
|
|
then return ...
|
|
else return method(...)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function dobefore(iceptor, request, method, ...)
|
|
local operation = iceptor.before
|
|
if operation
|
|
then return doafter(iceptor, request, method, operation(iceptor, request, ...))
|
|
else return doafter(iceptor, request, method, ...)
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
Wrapper = oo.class()
|
|
|
|
function Wrapper:__init(object)
|
|
local name = object.__name
|
|
object.__init = false
|
|
object.__methodkey = " method"..name
|
|
object.__indexkey = " index"..name
|
|
object.__newindexkey = " newindex"..name
|
|
object.__callkey = " call"..name
|
|
return oo.rawnew(self, object)
|
|
end
|
|
|
|
MethodCache = ObjectCache()
|
|
function MethodCache:retrieve(method)
|
|
return function(self, ...)
|
|
local object = self:__get()
|
|
local iceptor = rawget(self, " method") or self.__factory[self.__methodkey]
|
|
if iceptor then
|
|
local request = {
|
|
context = self.__context,
|
|
port = self.__name,
|
|
object = object,
|
|
method = method,
|
|
event = "method",
|
|
}
|
|
return dobefore(iceptor, request, method, object, ...)
|
|
end
|
|
return method(object, ...)
|
|
end
|
|
end
|
|
|
|
local function getfield(table, field)
|
|
return table[field]
|
|
end
|
|
function Wrapper:__index(field)
|
|
-- NOTE: retrieve class members first
|
|
local class = oo.classof(self)
|
|
if class[field] then return class[field] end
|
|
|
|
local object = self:__get()
|
|
local factory = self.__factory
|
|
local iceptor = rawget(self, " index") or factory[self.__indexkey]
|
|
|
|
local value
|
|
if iceptor then
|
|
local request = {
|
|
context = self.__context,
|
|
port = self.__name,
|
|
object = object,
|
|
field = field,
|
|
event = "index",
|
|
}
|
|
value = dobefore(iceptor, request, getfield, object, field)
|
|
else
|
|
value = object[field]
|
|
end
|
|
|
|
if type(value) == "function" then
|
|
value = MethodCache[value]
|
|
end
|
|
|
|
return value
|
|
end
|
|
|
|
local function setfield(table, field, value)
|
|
table[field] = value
|
|
end
|
|
function Wrapper:__newindex(field, value)
|
|
local object = self:__get()
|
|
local factory = self.__factory
|
|
local interceptor = rawget(self, " newindex") or factory[self.__newindex]
|
|
if interceptor then
|
|
local request = {
|
|
context = self.__context,
|
|
port = self.__name,
|
|
object = object,
|
|
field = field,
|
|
event = "newindex",
|
|
}
|
|
dobefore(iceptor, request, setfield, object, field, value)
|
|
else
|
|
object[field] = value
|
|
end
|
|
end
|
|
|
|
function Wrapper:__call(...)
|
|
local object = self:__get()
|
|
local factory = self.__factory
|
|
local iceptor = rawget(self, " call") or factory[self.__callkey]
|
|
if iceptor then
|
|
local request = {
|
|
context = self.__context,
|
|
port = self.__name,
|
|
object = object,
|
|
event = "call",
|
|
}
|
|
return dobefore(iceptor, request, object, ...)
|
|
else
|
|
return object(...)
|
|
end
|
|
end
|
|
|
|
function Wrapper:__intercept(event, iceptor)
|
|
rawset(self, " "..event, iceptor)
|
|
end
|
|
|
|
function intercept(scope, port, event, iceptor)
|
|
local container = rawget(scope, "__container")
|
|
local wrapper = container and container[port]
|
|
if oo.instanceof(wrapper, Wrapper)
|
|
then rawset(wrapper, " "..event, iceptor)
|
|
else scope[" "..event..port] = iceptor
|
|
end
|
|
end
|
|
|
|
--[[----------------------------------------------------------------------------
|
|
|
|
-- Intercept at all ports of all components
|
|
loop.component.intercepted.Wrapper:__intercept(event, iceptor)
|
|
-- Intercept all facets of all components
|
|
loop.component.intercepted.Facet:__intercept(event, iceptor)
|
|
-- Intercept a particular port of a component type
|
|
loop.component.intercepted.intercept(MyCompType, "MyPort", event, iceptor)
|
|
-- Intercept a particular port of a component implementation
|
|
loop.component.intercepted.intercept(MyCompFactory, "MyPort", event, iceptor)
|
|
-- Intercept a particular port of a component instance
|
|
loop.component.intercepted.intercept(MyComponent, "MyPort", event, iceptor)
|
|
|
|
----------------------------------------------------------------------------]]--
|
|
|
|
Facet = oo.class({}, Wrapper)
|
|
|
|
function Facet:__init(state, key, context)
|
|
local wrapper = Wrapper.__init(self, {
|
|
__state = state,
|
|
__context = context,
|
|
__key = key,
|
|
__name = tostring(key),
|
|
__factory = state.__factory,
|
|
})
|
|
wrapper:__bind(state[key] or state.__component[key] or state.__component)
|
|
return wrapper
|
|
end
|
|
|
|
function Facet:__bind(port)
|
|
self.__state[self.__key] = port
|
|
end
|
|
|
|
function Facet:__get()
|
|
return self.__state[self.__key]
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
Receptacle = oo.class({}, Wrapper)
|
|
|
|
function Receptacle:__init(state, key, context)
|
|
local wrapper = Wrapper.__init(self, {
|
|
__state = state,
|
|
__context = context,
|
|
__key = key,
|
|
__name = tostring(key),
|
|
__factory = state.__factory,
|
|
})
|
|
wrapper:__bind(state[key])
|
|
return wrapper
|
|
end
|
|
|
|
function Receptacle:__bind(port)
|
|
rawset(self, "__external", port)
|
|
self.__state[self.__key] = port and self
|
|
end
|
|
|
|
function Receptacle:__unbind()
|
|
rawset(self, "__external", nil)
|
|
self.__state[self.__key] = nil
|
|
end
|
|
|
|
function Receptacle:__get()
|
|
return rawget(self, "__external")
|
|
end
|
|
|
|
local function iterator(self, done)
|
|
if not done then return 1, self:__get() end
|
|
end
|
|
function Receptacle:__all()
|
|
return iterator, self
|
|
end
|
|
|
|
Receptacle.__hasany = Receptacle.__get
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
local ReceptacleWrapper = oo.class()
|
|
|
|
function ReceptacleWrapper:__init(state, key, context)
|
|
self = oo.rawnew(self, state[key])
|
|
|
|
local connections
|
|
for key, port in self.__receptacle:__all() do
|
|
connections = {}
|
|
for key, port in self.__receptacle:__all() do
|
|
connections[key] = port
|
|
end
|
|
break
|
|
end
|
|
|
|
rawset(self, "__new", oo.class(Wrapper:__init{
|
|
__get = Receptacle.__get,
|
|
__state = state,
|
|
__context = context,
|
|
__key = key,
|
|
__name = tostring(key),
|
|
__factory = state.__factory,
|
|
}, Wrapper))
|
|
|
|
if connections then
|
|
for key, port in pairs(connections) do
|
|
self.__receptacle:__unbind(key)
|
|
self:__bind(port, key)
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
function ReceptacleWrapper:__index(key)
|
|
return getmetatable(self)[key] or self:__get(key)
|
|
end
|
|
|
|
function ReceptacleWrapper:__newindex(key, value)
|
|
if value == nil
|
|
then self:__unbind(key)
|
|
else self:__bind(value, key)
|
|
end
|
|
end
|
|
|
|
function ReceptacleWrapper:__bind(port, key)
|
|
return self.__receptacle:__bind(self.__new{ __external = port }, key)
|
|
end
|
|
|
|
function ReceptacleWrapper:__unbind(key)
|
|
return rawget(self.__receptacle:__unbind(key), "__external")
|
|
end
|
|
|
|
function ReceptacleWrapper:__get(key)
|
|
local element = self.__receptacle:__get(key)
|
|
if element then return rawget(element, "__external") end
|
|
end
|
|
|
|
function ReceptacleWrapper:__all()
|
|
local iterator, state, key = self.__receptacle:__all()
|
|
local element
|
|
return function(state, key)
|
|
key, element = iterator(state, key)
|
|
if key and element then return key, rawget(element, "__external") end
|
|
end, state, key
|
|
end
|
|
|
|
function ReceptacleWrapper:__intercept(interceptor, event, field)
|
|
return self.__new:__intercept(interceptor, event, field)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
MultipleReceptacle = oo.class()
|
|
|
|
function MultipleReceptacle:__init(segments, name, context)
|
|
segments[name] = { __receptacle = oo.rawnew(self, segments[name]) }
|
|
return ReceptacleWrapper(segments, name, context)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
ListReceptacle = oo.class({}, MultipleReceptacle, base.ListReceptacle)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
HashReceptacle = oo.class({}, MultipleReceptacle, base.HashReceptacle)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
SetReceptacle = oo.class({}, MultipleReceptacle, base.SetReceptacle)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
_M[Facet ] = "Facet"
|
|
_M[Receptacle ] = "Receptacle"
|
|
_M[ListReceptacle] = "ListReceptacle"
|
|
_M[HashReceptacle] = "HashReceptacle"
|
|
_M[SetReceptacle ] = "SetReceptacle"
|