Code covered by the BSD License  

Highlights from
MagicListener

from MagicListener by Benoit Charles
MagicListener object

MagicListener
% MagicListener object
%
%	MagicListener constructor creates a listener just like the addlistener
%	function. The difference is the constructor accepts one more input, one
%	object or a cell of objects, and the life cycle of the returned object
%	will be linked also on these objects.
%
%	Exemple:
%
%	Without MagicListener:
%
%	[Class1.m]
%	classdef Class1 < handle
%		events
%			myEvent
%		end
%	end
%
%	[Class2.m]
%	classdef Class2 < handle
%		methods
%			function this = Class2( obj1 )
%				addlistener( obj1, 'myEvent', @(H,E) this.callback() );
%			end
%			function callback( this )
%				disp( 'CALLBACK!' );
%			end
%		end
%	end
%
%	>> obj1 = Class1();
%	>> obj2 = Class2( obj1 );
%	>> notify( obj1, 'myEvent' );
%	CALLBACK!
%	>> delete( obj2 );
%	>> notify( obj1, 'myEvent' );
%	Warning: Error occurred while executing callback:
%	Invalid or deleted object.
%
%  With MagicListener:
%
%	[Class1.m]
%	classdef Class1 < handle
%		events
%			myEvent
%		end
%	end
%
%	[Class2.m]
%	classdef Class2 < handle
%		methods
%			function this = Class2( obj1 )
%				MagicListener( obj1, 'myEvent', @(H,E) this.callback(), this );
%			end
%			function callback( this )
%				disp( 'CALLBACK!' );
%			end
%		end
%	end
%
%	>> obj1 = Class1();
%	>> obj2 = Class2( obj1 );
%	>> notify( obj1, 'myEvent' );
%	CALLBACK!
%	>> delete( obj2 );
%	>> notify( obj1, 'myEvent' );
%

classdef MagicListener < handle
	
	%% Public properties
	
	properties( Dependent )
		Source
		EventName
		Callback
		Enabled
		Recursive
		
		LifeCycleObj
	end
	
	methods
		
		function set.Source( this, value )
			this.listenerObj.Source = value;
		end
		function value = get.Source( this )
			value = this.listenerObj.Source;
		end
		
		function set.EventName( this, value )
			this.listenerObj.EventName = value;
		end
		function value = get.EventName( this )
			value = this.listenerObj.EventName;
		end
		
		function set.Callback( this, value )
			this.listenerObj.Callback = value;
		end
		function value = get.Callback( this )
			value = this.listenerObj.Callback;
		end
		
		function set.Enabled( this, value )
			this.listenerObj.Enabled = value;
		end
		function value = get.Enabled( this )
			value = this.listenerObj.Enabled;
		end
		
		function set.Recursive( this, value )
			this.listenerObj.Source = value;
		end
		function value = get.Recursive( this )
			value = this.listenerObj.Recursive;
		end
		
		
		function set.LifeCycleObj( this, value )
			this.lifeCycleListenerObj.Source = value;
		end
		function value = get.LifeCycleObj( this )
			value = this.lifeCycleListenerObj.Source;
		end
		
		
	end
	
	%% Private properties
	
	properties( Access = private )
		listenerObj = [];
		lifeCycleListenerObj = [];
	end
		
	%% Constructor and destructor
	
	methods
		
		function this = MagicListener( source, varargin )
			% ml = MagicListener( source, [property], event, callback, [lifeCycleObj] )
			
			if nargin == 0
				return
			end
			
			import event.listener
			import event.proplistener
			
			% load input parameters
			error( nargchk( 3, 5, nargin, 'struct' ) );
			
			propertyEvent = false;
			lifecycle = {};
			
			if ischar( varargin{2} )
				propertyEvent = true;
				property = varargin{1};
				event = varargin{2};
				callback = varargin{3};
				if length( varargin ) == 4
					lifecycle = varargin{4};
				end
			else
				event = varargin{1};
				callback = varargin{2};
				if length( varargin ) == 3
					lifecycle = varargin{3};
				end
			end
			
			isDestroyEvent = strcmp( event, 'ObjectBeingDestroyed' );
			if isDestroyEvent
				finalCallback = @deleteAfterCallback;
			else
				finalCallback = @trycatchCallback;
			end
			
			% set source and lifeCycleObj in cell array
			
			if ~iscell( source )
				if numel( source ) == 1
					source = { source };
				else
					source = num2cell( source );
				end
			end
			
			if ~iscell( lifecycle )
				if numel( lifecycle ) == 1
					lifecycle = { lifecycle };
				else
					lifecycle = num2cell( lifecycle );
				end
			end
			
			if ~isDestroyEvent
				lifecycle = [ source lifecycle ];
			end
			
			% create listener
			if propertyEvent
				
				nbSource = length( source );
				properties = cell( 1, nbSource );
				
				for prop = 1:nbSource
					
					metaSource = metaclass( source{prop} );
					propSource = metaSource.Properties;
					
					numProp = cellfun( @(prop) strcmp( prop.Name, property ), propSource );
					
					properties{prop} = propSource{ numProp };
					
				end
				
				this.listenerObj = proplistener( source, properties, event, finalCallback );
			else
				this.listenerObj = listener( source, event, finalCallback );
			end
			
			% create lifecycle listener
			this.lifeCycleListenerObj = listener( lifecycle, 'ObjectBeingDestroyed', @(H,E) destroy() );
			
			MagicListener.manageMagicListener( 'add', this )
			
			function trycatchCallback( H, E )
				try
					callback( H, E )
				catch err
					getReport( err )
				end
			end
			
			function deleteAfterCallback( H, E )
				try
					callback( H, E )
				catch err
					getReport( err )
				end
				destroy();
			end
			
			function destroy()
				if isvalid( this )
					delete( this );
				end
			end
			
		end
		
		function delete( this )
			if isvalid( this )
				MagicListener.manageMagicListener( 'rem', this )
				delete( this.listenerObj );
				delete( this.lifeCycleListenerObj );
			end
		end
		
	end
	
	%% Kill 'em all!!
	
	methods( Static )
		
		function killall()
			MagicListener.manageMagicListener( 'remall' );
		end
		
	end
	
	%% Management (record and delete - static)
	
	methods( Static, Access = private )
		
		function manageMagicListener( action, magicListener )
			
			persistent recursiveFlag bufferSize buffer listeners listenersSize nbListeners
			if isempty( recursiveFlag )
				recursiveFlag = false;
				initialize()
			end
			
			function initialize()
				bufferSize = 100;
				temp(1, 1:bufferSize) = MagicListener();
				buffer = temp;
				
				listeners = buffer;
				listenersSize = bufferSize;
				nbListeners = 0;
			end
			
			if recursiveFlag
				return
			else
				recursiveFlag = true; %#ok<NASGU>
			end
			
			switch action
				
				case 'add'
					
					if nbListeners+1 > listenersSize
						if isempty( buffer )
							initialize();
						else
							listeners = [ listeners buffer ];
							listenersSize = listenersSize + bufferSize;
						end
					end
					
					nbListeners = nbListeners + 1;
					listeners( nbListeners ) = magicListener;
					
				case 'rem'
					
					listeners( listeners == magicListener ) = [];
					nbListeners = nbListeners - 1;
					listenersSize = listenersSize - 1;
					
				case 'remall'
					
					for i = 1:nbListeners
						delete( listeners(i) );
					end
					
					buffer = [];
					listeners = [];
					listenersSize = 0;
					nbListeners = 0;
					
			end
			
			recursiveFlag = false;
			
		end
		
	end
	
end

Contact us