#--
# =============================================================================
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * The names of its contributors may not be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# =============================================================================
#++
require 'copland/errors'
module Copland
# Represents an interceptor associated with a service point, not a
# _service_. When the service point with which this interceptor is associated
# is instantiated, the #instantiate method of this object will be invoked to
# obtain the actual interceptor instance.
#
# This wrapper object encapsulates the "before" and "after" lists, as well,
# which are used to determine the order in which the interceptors on the
# associated service point are invoked for each intercepted method call.
class Interceptor
# The service point with which this interceptor is associated.
attr_reader :owner
# The service point of the factory that will return the interceptor
# instance on demand.
attr_reader :point
# The array of interceptor service point _names_ (not services) that
# this interceptor should come before.
attr_reader :before
# The array of interceptor service point _names_ (not services) that
# this interceptor should come after.
attr_reader :after
# A hash of the constructor parameters that should be sent to the factory
# when instantiating the interceptor service. This attribute is the value
# of the hash prior to processing by any schema.
attr_reader :construction_parms
# Create a new interceptor on the given +owner+ service point, with the
# associated +definition+ map. The map _must_ include a value named
# "service", which should be the name of the service point of the factory
# that will be used to instantiate the interceptor when needed. If
# "before" or "after" exist, they are interpreted to be the "before" and
# "after" attributes of this interceptor. (If either of them are strings,
# they will be converted into an array of one element; otherwise, they
# should be arrays.)
#
# Any other elements in the +definition+ map will be used as constructor
# parameters.
def initialize( owner, definition )
@owner = owner
unless definition[ "service" ]
raise MissingImplementationException,
"interceptor for #{owner.full_name} needs 'service' element"
end
@point = owner.find_service_point( definition[ "service" ] )
@before = [ *( definition[ "before" ] || [] ).dup ]
@after = [ *( definition[ "after" ] || [] ).dup ]
definition = definition.dup
definition.delete "service"
definition.delete "before"
definition.delete "after"
@construction_parms = definition
schema = point.schema
if schema.respond_to?( :validate )
schema.validate @point, @owner, @construction_parms
end
end
# Return an instance of the interceptor service that is wrapped by this
# object. This is done by invoking the #create_instance method on the
# factory service. If the factory service point has a schema associated
# with it, it will be used to pre-process the parameters.
def instantiate
@factory = @point.instance unless @factory
parms = @construction_parms
schema = @point.schema
if schema.respond_to?( :process )
parms = schema.process( point, owner, parms )
end
@factory.create_instance( @owner, parms )
end
# Returns +true+ if +self+ should be ordered before +interceptor+.
def before?( interceptor )
a = before.include?( interceptor.point.full_name )
b = interceptor.after.include?( point.full_name )
return a || b
end
# Returns +true+ if +self+ should be ordered after +interceptor+.
def after?( interceptor )
a = after.include?( interceptor.point.full_name )
b = interceptor.before.include?( point.full_name )
return a || b
end
end
end