% MAGICSYNCHRONIZER Creates an object to synchronize a set of properties.
%
% ms = MagicSynchronizer( callback, ...
% [ 'EXE' ], ...
% [ 'IN', obj, prop, [ prop2, [...] ], [ obj2, ... ] ], ...
% [ 'OUT', obj, prop, [ prop2, [...] ], [ obj2, ... ] ], ...
% [ 'DEP', obj, [ obj2 ... ] ], ...
% [ 'CHECKIN', check ], ...
% [ 'NAME', name ] );
% creates a new synchronizer
%
% The callback is a function handle, which doesn't take any argument and
% doesn't return any value. This callback will be called when any 'input'
% properties has been changed ('IN' set).
%
% 'DEP' is an optionnal set, and can be use to link the life cycle of
% the synchronizer with another objects.
%
% 'EXE' is an optional flag. When it's given, the callback is called once
% in the constructor.
%
% 'CHECKIN' is an optional boolean (true per default). When it's false,
% the synchronizer don't check if the input has been changed. You can
% change directly this value when the synchronizer is created whith the
% property 'CheckIn'.
%
% 'NAME' is an optionnal argument, only useful for debugging.
%
% If the string 'copy' is given instead the callback, a default callback
% which copy "out" properties to "in" emplacements is used.
%
% When a synchronizer is created, it searchs automatically from what
% synchronizer it depends, and what synchronizers are depending from it.
% So, when a synchronizer is launch, it launchs its children too.
%
% You can't launch a specific synchronizer, but you can notify you have
% changed some property values, and the synchronizers which are listening
% theses properties will be lauch. To notify some changements, you have
% to use the class function MagicSynchronizer.synchronize:
% MagicSynchronizer.synchronize( [obj] | [obj,'prop',['prop2',...]], ... );
%
% Synchronizers save the last values of properties: a synchronizer calls
% the callback only if at least one input property is modified.
%
% The callback can interrupt the progress (e.g. the user has clicked on
% the famous "cancel" button), with throwing a
% MagicSynchronizer.interruptionError error. The goal of synchronizer is
% to keep a homogeous state beetwen all given properties. So, when this
% error appends, the inputs properties are restored to the last "stable"
% version, also the properties of the synchronizer's parents. If any else
% error appends, the error is immediatly rethrown: the properties are not
% synchronized.
%
% Sometimes, during the developpment of a programm, you need to delete
% every synchronizers. To do that:
% MagicSynchronizer.nukeall();
% Of course, absolutly all synchronizer will be destructed.
% This command can help a "clear classes".
%
classdef MagicSynchronizer < handle
%% ### INSTANCE PROPERTIES AND METHODS
%% Properties
properties
CheckIn
end
properties( Access = private )
inid
outid
parents
children
hasToBeExecutedFlag = 0;
callback
lifeCycleListenerObj
executionIndex = 0;
end
properties
Name = '';
end
%% Constructor
methods
function this = MagicSynchronizer( callback, varargin )
persistent cycleError argumentError
if isempty( cycleError )
cycleError = MagicSynchronizer.cycleError;
cycleError.message = 'You''re killing me? You''re creating a cyclic synchronizer!';
argumentError = struct( ...
'identifier', 'MagicSynchronizer:UnexpectedArguments', ...
'message', 'Unexpected arguments' );
end
if nargin == 0
return
end
% read arguments
currentState = '';
in = struct( 'object', {}, 'property', {} );
out = struct( 'object', {}, 'property', {} );
depends = {};
name = '';
checkin = true;
exeflag = false;
i = 1; nbargs = length( varargin );
while i <= nbargs
arg = varargin{i};
if ischar( arg ) && any( strcmp( arg, { 'IN', 'OUT', 'DEP', 'NAME', 'CHECKIN' } ) )
currentState = arg;
elseif ischar( arg ) && strcmp( arg, 'EXE' )
exeflag = true;
elseif strcmp( currentState, 'IN' ) && isa( varargin{i}, 'handle' ) && ischar( varargin{i+1} )
in( end+1 ) = struct( 'object', varargin{i}, 'property', varargin{i+1} ); %#ok<AGROW>
i = i+1;
elseif strcmp( currentState, 'IN' ) && ischar( varargin{i} )
in( end+1 ) = struct( 'object', in(end).object, 'property', varargin{i} ); %#ok<AGROW>
elseif strcmp( currentState, 'OUT' ) && isa( varargin{i}, 'handle' ) && ischar( varargin{i+1} )
out( end+1 ) = struct( 'object', varargin{i}, 'property', varargin{i+1} ); %#ok<AGROW>
i = i+1;
elseif strcmp( currentState, 'OUT' ) && ischar( varargin{i} )
out( end+1 ) = struct( 'object', out(end).object, 'property', varargin{i} ); %#ok<AGROW>
elseif strcmp( currentState, 'DEP' ) && isa( varargin{i}, 'handle' )
depends{ end+1 } = varargin{i}; %#ok<AGROW>
elseif strcmp( currentState, 'CHECKIN' ) && isa( varargin{i}, 'logical' )
checkin = varargin{i};
elseif strcmp( currentState, 'NAME' ) && ischar( varargin{i} )
name = varargin{i};
else
error( argumentError );
end
i = i+1;
end
nbin = length( in );
nbout = length( out );
% record the checkin flag
this.CheckIn = checkin;
% record name
this.Name = name;
% record callback
function copyCallback( nb, in, out )
for iii = 1:nb
out(iii).object.( out(iii).property ) = in(iii).object.( in(iii).property );
end
end
if ischar( callback ) && strcmp( callback, 'copy' ) && nbin == nbout
callback = @() copyCallback( nbin, in, out );
end
this.callback = callback;
% run once the callback
if exeflag
callback();
end
% get all depending object
function addDepends( obj )
if ~any( cellfun( @(o) o==obj, depends ) )
depends = [ depends {obj } ];
end
end
for i = 1:nbin, addDepends( in(i).object ); end
for i = 1:nbout, addDepends( out(i).object ); end
% create life cycle listener
this.lifeCycleListenerObj = event.listener( depends, 'ObjectBeingDestroyed', @(H,E) destroy() );
function destroy()
if isvalid( this )
delete( this );
end
end
% record in and out properties
idin = zeros( 1, nbin );
for i = 1:nbin
idin( i ) = MagicSynchronizer.propertiesManager( 'add', 'in', in(i).object, in(i).property );
end
this.inid = idin;
idout = zeros( 1, nbout );
for i = 1:nbout
idout( i ) = MagicSynchronizer.propertiesManager( 'add', 'out', out(i).object, out(i).property );
end
this.outid = idout;
if ~isempty( intersect( idin, idout ) )
error( cycleError );
end
% record and get parents and children
[ this.parents, this.children ] = MagicSynchronizer.synchronizersManager( 'add', this );
if ~isempty( this.parents ), this.parents.addChild( this ); end
if ~isempty( this.children ), this.children.addParent( this ); end
end
end
%% Destructor
methods
function delete( this )
MagicSynchronizer.synchronizersManager( 'rem', this );
if ~isempty( this.parents )
this.parents.remChild( this );
this.parents = [];
end
if ~isempty( this.children )
this.children.remParent( this );
this.children = [];
end
end
end
%% Display
methods
function str = tostring( this )
function final = join( elts, j )
if nargin == 1, j = ' '; end
if isempty( elts )
final = '';
elseif length( elts ) == 1
final = elts{1};
else
final = [ elts{1} sprintf( [ j, '%s' ], elts{2:end} ) ];
end
end
function str = idprop2string( idprop )
str = '';
[ obj, prop ] = MagicSynchronizer.propertiesManager( 'get', idprop );
if ~isempty( prop )
meta = metaclass( obj );
str = sprintf( '%s.%s', meta.Name, prop );
end
end
strexe = '';
if this.hasToBeExecutedFlag ~= 0
strexe = '(*) ';
end
strname = '';
if ~isempty( this.Name )
strname = sprintf( '%s : ', this.Name );
end
strin = join( arrayfun( @(id) idprop2string(id), this.inid, 'UniformOutput', false ), ', ' );
strout = join( arrayfun( @(id) idprop2string(id), this.outid, 'UniformOutput', false ), ', ' );
str = sprintf( '%s%s%s > %s > %s', strexe, strname, strin, func2str( this.callback ), strout );
end
function display( array )
function final = join( elts, j )
if nargin == 1, j = ' '; end
if isempty( elts )
final = '';
elseif length( elts ) == 1
final = elts{1};
else
final = [ elts{1} sprintf( [ j, '%s' ], elts{2:end} ) ];
end
end
function c = arraytostring( syncs )
l = length( syncs );
c = cell( 1, l );
for j = 1:l
c{j} = syncs(j).tostring();
end
end
larray = length( array );
fprintf( '\n\tNB SYNCHRONIZERS : %d\n\n', larray );
sep = repmat( '-', 1, 30 );
for i = 1:larray
sync = array(i);
fprintf( '%d %s\n%s\n\nPARENTS:\n%s\n\nCHILDREN:\n%s\n\n', ...
i, sep, ...
sync.tostring(), ...
join( arraytostring( sync.parents ), '\n' ), ...
join( arraytostring( sync.children ), '\n' ) );
end
end
end
%% Family matters
methods( Access = private )
function addParent( syncs, sync )
for i = 1:length( syncs )
syncs(i).parents( end+1 ) = sync;
end
end
function remParent( syncs, sync )
for i = 1:length( syncs )
if isvalid( syncs(i) )
syncs(i).parents( syncs(i).parents==sync ) = [];
end
end
end
function addChild( syncs, sync )
for i = 1:length( syncs )
syncs(i).children( end+1 ) = sync;
end
end
function remChild( syncs, sync )
for i = 1:length( syncs )
if isvalid( syncs(i) )
syncs(i).children( syncs(i).children==sync ) = [];
end
end
end
function path = searchALoop( this, pathFrom )
path = [];
for i = 1:length( pathFrom )
if pathFrom(i) == this
path = [ pathFrom(i:end) this ];
return
end
end
pathFrom( end+1 ) = this;
if ~isempty( this.parents )
for i = 1:length( this.parents )
path = this.parents(i).searchALoop( pathFrom );
if ~isempty( path )
return
end
end
end
end
end
%% Execution
methods( Access = private )
function setToBeExecuted( syncs, flagValue, registerCallback )
for i = 1:length( syncs )
if syncs(i).hasToBeExecutedFlag == 0
syncs(i).hasToBeExecutedFlag = flagValue;
syncs(i).executionIndex = registerCallback( syncs(i) );
if ~isempty( syncs(i).children )
syncs(i).children.setToBeExecuted( flagValue, registerCallback );
end
elseif syncs(i).hasToBeExecutedFlag ~= flagValue
error( MagicSynchronizer.doubleExecutionError.identifier, ...
'You try to execute this synchonizer twice:\n%s', syncs(i).tostring() );
end
end
end
function setToBeNotExecuted( syncs )
for i = 1:length( syncs )
syncs(i).hasToBeExecutedFlag = 0;
if ~isempty( syncs(i).children )
syncs(i).children.setToBeNotExecuted();
end
end
end
function execute( this, checkExecutionCallback )
persistent interruptionErrorId
if isempty( interruptionErrorId )
interruptionErrorId = MagicSynchronizer.interruptionError.identifier;
end
% don't run if not need
if ~isvalid( this ) || ( this.hasToBeExecutedFlag == 0 )
return
end
% test if any parent needs to be executed before
if ~isempty( this.parents ) && any( [ this.parents.hasToBeExecutedFlag ]~=0 )
return
end
checkExecutionCallback( this.executionIndex );
ch = this.children;
% test if the callback really needs to be executed
exeFlag = ~this.CheckIn || any( MagicSynchronizer.propertiesManager( 'test', this.inid ) );
% execute
if exeFlag
try
this.callback();
catch err
MagicSynchronizer.propertiesManager( 'check', this.outid );
rethrow( err );
end
if isvalid( this ), MagicSynchronizer.propertiesManager( 'check', this.outid ); end
end
if isvalid( this ), this.hasToBeExecutedFlag = 0; end
% execute children
ch = ch( isvalid( ch ) );
for i = 1:length( ch )
ch(i).execute( checkExecutionCallback );
end
end
end
%% ### CLASS PROPERTIES AND METHODS
%% Interruption error
properties( Constant )
interruptionError = struct( 'identifier', 'MagicSynchronizer:Interrupted' );
cycleError = struct( 'identifier', 'MagicSynchronizer:CycleFound' );
doubleExecutionError = struct( 'identifier', 'MagicSynchronizer:DoubleExecutionError' );
end
%% Launch synchronizers
methods( Static )
function varargout = exeflagManager( order, varargin )
persistent usedFlag
switch order
case 'get'
flag = find( [ ~usedFlag true ], 1 );
usedFlag( flag ) = true;
varargout = { flag };
case 'release'
usedFlag( varargin{1} ) = false;
end
end
function synchronize( varargin )
persistent cycleErrorId interruptionErrorId
if isempty( cycleErrorId )
cycleErrorId = MagicSynchronizer.cycleError.identifier;
interruptionErrorId = MagicSynchronizer.interruptionError.identifier;
end
waitbarflag = false;
if ischar( varargin{1} ) && strcmp( varargin{1}, 'WAITBAR' )
waitbarflag = true;
varargin = varargin(2:end);
end
syncprops = struct( ...
'obj', {}, ...
'prop', {}, ...
'ids', {} );
prevIsObj = false;
for i = 1:length( varargin )
if ischar( varargin{i} )
if ~prevIsObj
syncprops( end+1 ).obj = syncprops( end ).obj; %#ok<AGROW>
end
syncprops( end ).prop = varargin{i};
prevIsObj = false;
else
syncprops( end+1 ).obj = varargin{i}; %#ok<AGROW>
prevIsObj = true;
end
end
% get id of properties
for i = 1:length( syncprops )
if isempty( syncprops(i).prop )
syncprops(i).ids = MagicSynchronizer.propertiesManager( 'object', syncprops(i).obj );
else
syncprops(i).ids = MagicSynchronizer.propertiesManager( 'property', syncprops(i).obj, syncprops(i).prop );
end
end
ids = unique( [ syncprops.ids ] );
if isempty( ids )
return
end
% check properties
MagicSynchronizer.propertiesManager( 'check', ids )
% get synchronizers
syncs = MagicSynchronizer.synchronizersManager( 'depends', ids );
% initialize synchronizers
exeflag = MagicSynchronizer.exeflagManager( 'get' );
bufferSize = 20;
buffer(1, 1:bufferSize) = MagicSynchronizer();
listSync = buffer;
syncsSize = bufferSize;
nbSyncs = 0;
function ind = registerFct( sync )
if nbSyncs+1 > syncsSize
listSync = [ listSync buffer ];
syncsSize = syncsSize + bufferSize;
end
nbSyncs = nbSyncs + 1;
listSync( nbSyncs ) = sync;
ind = nbSyncs;
end
try
syncs.setToBeExecuted( exeflag, @registerFct );
catch err
listSync = listSync( 1:nbSyncs );
listSync = listSync( isvalid( listSync ) );
listSync.setToBeNotExecuted();
MagicSynchronizer.exeflagManager( 'release', exeflag );
rethrow( err );
end
listSync = listSync( 1:nbSyncs );
% run synchronizers
checkList = false( 1, nbSyncs );
function checkExecutionFct( ind )
checkList( ind ) = true;
end
if waitbarflag
hw = waitbar( 0, 'Please wait...' );
end
try
for i = 1:length( syncs )
syncs(i).execute( @checkExecutionFct );
if waitbarflag
waitbar( mean( checkList ), hw );
end
end
catch err
if strcmp( err.identifier, interruptionErrorId )
runnedSync = listSync( checkList ); runnedSync = runnedSync( isvalid( runnedSync ) );
ids = unique( [ runnedSync.inid runnedSync.outid ] );
ids = ids( MagicSynchronizer.propertiesManager( 'test', ids ) );
MagicSynchronizer.propertiesManager( 'restore', ids )
return
else
listSync = listSync( isvalid( listSync ) );
listSync.setToBeNotExecuted();
MagicSynchronizer.exeflagManager( 'release', exeflag );
getReport( err )
rethrow( err );
end
end
if waitbarflag
delete( hw )
end
% get new properties values
runnedSync = listSync( checkList ); runnedSync = runnedSync( isvalid( runnedSync ) );
ids = unique( [ runnedSync.inid runnedSync.outid ] );
MagicSynchronizer.propertiesManager( 'save', ids )
% release the flag
MagicSynchronizer.exeflagManager( 'release', exeflag );
% check loops
if ~all( checkList )
% search a loop
path = listSync( find( ~checkList, 1 ) ).searchALoop( MagicSynchronizer.empty(1,0) );
% create a string from linked properties
nbl = length( path );
linkInProp = { path.inid };
linkOutProp = { path.outid };
linkProp = cell( 1, nbl );
for i = 1:nbl
strin = join( arrayfun( @(id) idprop2string(id), linkInProp{i}, 'UniformOutput', false ), ', ' );
strout = join( arrayfun( @(id) idprop2string(id), linkOutProp{i}, 'UniformOutput', false ), ', ' );
linkProp{nbl-i+1} = sprintf( '%s -> %s', strin, strout );
end
links = join( linkProp, '\n' );
% create error
error( cycleErrorId, 'A loop has been found!\n%s', links );
end
function final = join( elts, j )
if nargin == 1, j = ' '; end
if isempty( elts )
final = '';
elseif length( elts ) == 1
final = elts{1};
else
final = [ elts{1} sprintf( [ j, '%s' ], elts{2:end} ) ];
end
end
function str = idprop2string( idprop )
str = '';
[ obj, prop ] = MagicSynchronizer.propertiesManager( 'get', idprop );
if ~isempty( prop )
meta = metaclass( obj );
str = sprintf( '%s.%s', meta.Name, prop );
end
end
end
end
%% Nuke 'em all!!
methods( Static )
function nukeall()
MagicSynchronizer.synchronizersManager( 'remall' );
MagicSynchronizer.propertiesManager( 'remall' );
end
end
%% Properties manager
methods( Static, Access = private )
% id = MagicSynchronizer.propertiesManager( 'add', ['in'|'out'], obj, prop )
% ids = MagicSynchronizer.propertiesManager( 'object', obj )
% id = MagicSynchronizer.propertiesManager( 'property', obj, prop )
% [ obj, prop ] = MagicSynchronizer.propertiesManager( 'get', id )
% MagicSynchronizer.propertiesManager( 'save', [ ids ] )
% MagicSynchronizer.propertiesManager( 'restore', [ ids ] )
% MagicSynchronizer.propertiesManager( 'check', [ ids ] )
% [ isModified ] = MagicSynchronizer.propertiesManager( 'test', [ idsToCheck ] )
% MagicSynchronizer.propertiesManager( 'remall' )
function varargout = propertiesManager( order, varargin )
persistent propBufferSize propBuffer properties propSize nbProp
persistent objBufferSize objBuffer objects objSize nbObjects
persistent lastid
if isempty( lastid )
init()
end
function init()
propBufferSize = 100;
propBuffer = repmat( struct( ...
'id', [], ...
'object', [], ...
'name', [], ...
'state', [], ...
'verified', [], ...
'modified', [] ), 1, propBufferSize );
properties = propBuffer;
propSize = propBufferSize;
nbProp = 0;
objBufferSize = 10;
objBuffer = repmat( struct( ...
'object', [], ...
'inids', [], ...
'outids', [], ...
'lifeCycleListener', [] ), 1, objBufferSize );
objects = objBuffer;
objSize = objBufferSize;
nbObjects = 0;
lastid = 0;
end
switch order
case 'remall'
% reinitialize
init();
varargout = {};
case 'add'
% add a new property
[ flag, obj, prop ] = deal( varargin{:} );
% search indexes of an existing property
[ indObj, indProp ] = searchIndex( obj, prop );
% create a new object
if isempty( indObj )
if nbObjects+1 > objSize
objects = [ objects objBuffer ];
objSize = objSize + objBufferSize;
end
nbObjects = nbObjects+1;
objects( nbObjects ).object = obj;
objects( nbObjects ).lifeCycleListener = event.listener( obj, 'ObjectBeingDestroyed', @(H,E) deleteObject( obj ) );
indObj = nbObjects;
end
% create a new property
if isempty( indProp )
lastid = lastid + 1;
propid = lastid;
if nbProp+1 > propSize
properties = [ properties propBuffer ];
propSize = propSize + propBufferSize;
end
nbProp = nbProp + 1;
properties( nbProp ).id = propid;
properties( nbProp ).object = obj;
properties( nbProp ).name = prop;
properties( nbProp ).state = obj.( prop );
properties( nbProp ).modified = false;
else
propid = properties( indProp ).id;
end
% record id in the object struct
if strcmp( flag, 'in' )
objects( indObj ).inids = unique( [ objects( indObj ).inids propid ] );
else
objects( indObj ).outids = unique( [ objects( indObj ).outids propid ] );
end
varargout = { propid };
case 'save'
% save new values of properties
ids = varargin{1};
[ isrec, loc ] = ismember( ids, [ properties(1:nbProp).id ] );
for k = loc( isrec )
newState = properties(k).object.( properties( k ).name );
properties(k).state = newState;
properties(k).modified = false;
end
case 'restore'
% restore saved properties values
ids = varargin{1};
[ isrec, loc ] = ismember( ids, [ properties(1:nbProp).id ] );
for k = loc( isrec )
properties(k).object.( properties( k ).name ) = properties(k).state;
properties(k).modified = false;
end
case 'check'
% check if properties has been modified
ids = varargin{1};
[ isrec, loc ] = ismember( ids, [ properties(1:nbProp).id ] );
for k = loc( isrec )
newState = properties( k ).object.( properties( k ).name );
properties( k ).modified = ~isequal( newState, properties( k ).state );
end
case 'test'
% return if properties has been modified
ids = varargin{1};
[ isrec, loc ] = ismember( ids, [ properties(1:nbProp).id ] );
ret = false( 1, length( ids ) );
ret( isrec ) = [ properties( loc(isrec) ).modified ];
varargout = { ret };
case 'object'
% return all 'in' id of an object
obj = varargin{1};
indObj = find( cellfun( @(o) o==obj, { objects(1:nbObjects).object } ), 1 );
if ~isempty( indObj )
varargout = { objects( indObj ).inids };
else
varargout = { [] };
end
case 'property'
% return id of a specific property
[ obj, prop ] = deal( varargin{:} );
[ indObj, indProp ] = searchIndex( obj, prop );
if ~isempty( indProp )
varargout = { properties( indProp ).id };
else
varargout = { [] };
end
case 'get'
% return object and property from an id
id = varargin{1};
[ isrec, loc ] = ismember( id, [ properties(1:nbProp).id ] );
if isrec
varargout = { properties(loc).object, properties(loc).name };
else
varargout = { [], '' };
end
end
% search indexes from object and name property
function [ indObj, indProp ] = searchIndex( obj, prop )
% search the object
indObj = find( cellfun( @(o) o==obj, { objects(1:nbObjects).object } ), 1 );
if isempty( indObj )
indProp = [];
return
end
% search the property
idsProp = unique( [ objects( indObj ).inids objects( indObj ).outids ] );
inds = find( ismember( [ properties( 1:nbProp ).id ], idsProp ) );
indProp = inds( find( strcmp( prop, { properties( inds ).name } ), 1 ) );
end
% object has been deleted
function deleteObject( obj )
% search object
indObj = find( cellfun( @(o) o==obj, { objects(1:nbObjects).object } ), 1 );
if isempty( indObj )
return
end
% delete properties
ids = unique( [ objects( indObj ).inids objects( indObj ).outids ] );
[ ind, loc ] = ismember( ids, [ properties(1:nbProp).id ] );
properties( loc( ind ) ) = [];
propSize = propSize - nnz(ind);
nbProp = nbProp - nnz(ind);
% delete object
objects( indObj ) = [];
objSize = objSize - 1;
nbObjects = nbObjects - 1;
end
end
end
%% Synchronizers manager
methods( Static, Access = private )
% [ parent, children ] = MagicSynchronizer.synchronizersManager( 'add', sync )
% MagicSynchronizer.synchronizersManager( 'rem', sync )
% [ children ] = MagicSynchronizer.synchronizersManager( 'depends', idprops )
% MagicSynchronizer.synchronizersManager( 'remall' )
function varargout = synchronizersManager( order, varargin )
persistent recursiveFlag bufferSize buffer syncs syncsSize nbSyncs
if isempty( recursiveFlag )
recursiveFlag = false;
initialize()
end
function initialize()
bufferSize = 100;
temp(1, 1:bufferSize) = MagicSynchronizer();
buffer = temp;
syncs = buffer;
syncsSize = bufferSize;
nbSyncs = 0;
end
if recursiveFlag
return
else
recursiveFlag = true; %#ok<NASGU>
end
switch order
case 'add'
sync = varargin{1};
if nbSyncs+1 > syncsSize
if isempty( buffer )
initialize();
else
syncs = [ syncs buffer ];
syncsSize = syncsSize + bufferSize;
end
end
nbSyncs = nbSyncs + 1;
syncs( nbSyncs ) = sync;
varargout = { getParents( sync.inid ), getChildren( sync.outid ) };
case 'depends'
varargout = { getChildren( varargin{1} ) };
case 'rem'
ind = syncs == varargin{1};
if any( ind )
syncs( ind ) = [];
nbSyncs = nbSyncs - nnz( ind );
syncsSize = syncsSize - nnz( ind );
end
varargout = {};
case 'remall'
for i = 1:nbSyncs
delete( syncs(i) );
end
buffer = [];
syncs = [];
syncsSize = 0;
nbSyncs = 0;
end
function parents = getParents( idprop )
ind = cellfun( @(ids) any( ismember( idprop, ids ) ), { syncs( 1:nbSyncs ).outid } );
parents = syncs( ind );
end
function children = getChildren( idprop )
ind = cellfun( @(ids) any( ismember( idprop, ids ) ), { syncs( 1:nbSyncs ).inid } );
children = syncs( ind );
end
recursiveFlag = false;
end
end
end