@doc alias="propagator" hierarchy="GMLDOM">
The ChangePropagator is an object used for propagation of the model modifications
in order to keep it consistent
This file is obsolete
(c) SAP AG 2003-2006. All rights reserved.
///////////////////////////////////////////////////////////////////////
// CLASS HEADER
Class ChangePropagator inherit Object;
<@doc>Gets the model organized by this object
readonly property model = ^gml:Model;
readonly property bPropagating = false;
<@doc scope="private">
Creates a new ChangePropagator object
The model that is propagated by this object
constructor (model)
this.model = model;
end
///////////////////////////////////////////////////////////////////////
// EVENT LISTENERS
listen onInsertElement for core.gml:Component
$ENV.propagator.callPropagate(evt, 'onInsertElement');
end
listen onRemoveElement for core.gml:Component
$ENV.propagator.callPropagate(evt, 'onRemoveElement');
end
listen onUpdateObject for core.gml:Port
if(evt.name != 'name') return; // check for changes of relevant properties
evt.child = evt.object;
$ENV.propagator.callPropagate(evt, 'onUpdateObject');
end
listen onInsertElement for core.gml:Configuration
$ENV.propagator.callPropagate(evt, 'onInsertElement');
end
listen onRemoveElement for core.gml:Configuration
$ENV.propagator.callPropagate(evt, 'onRemoveElement');
end
/*
listen onUpdateObject for core.gml:Field
evt.child = evt.object ;
$ENV.propagator.callPropagate(evt, 'onUpdateObject');
end
*/
<@doc scope="private">
Wrappes the propagate call in order to use predefined event types,
which are lowercased by event machanism of the browser
method callPropagate(evt, evtType)
var old_type = evt.type
evt.type = evtType;
this.propagate(evt);
evt.type = old_type;
end
/////////////////////////////////////////////////////////////////////
// METHODS
<@doc scope="private">
Main method which propogates all changes
method propagate(evt)
if(!evt) return;
if ($ENV.recorder && $ENV.recorder.getIsBusy()) {
return; //do not propagate if in the middle of undo, redo, rollback
}
try
{
if(this.bPropagating) return;
BEGIN('propagate change');
this.bPropagating = true;
var iterator=null, subI=null, obj;
if(!iterator)
iterator = $ENV.createObject("core.gml:ModelIterator");
/*
if( (ISA(evt.child, 'core.gml:Connectable'))
|| (ISA(evt.child, 'core.gml:Link')) ){
// Get tree of all objects, that should be notified (by target)
iterator = this.getPropagationTree(iterator, evt.child, evt, #[WALK_BY_CONNECTABLE_TRG]);
// Get tree of all objects, that should be notified (by source)
iterator = this.getPropagationTree(iterator, evt.child, evt, #[WALK_BY_CONNECTABLE_SRC]);
}
if (ISA(evt.child, 'core.gml:Field') ){
var fieldContainer = this.getFieldContainer(evt);
// Handle case : Infoeshape is in Port.
if ( ISA(fieldContainer , 'core.gml:Port')){
var dirPort = ISA(fieldContainer, 'core.gml:Outport') ?
#[WALK_BY_CONNECTABLE_SRC]:
#[WALK_BY_CONNECTABLE_TRG] ;
this.propagateByUsages(fieldContainer , evt );
// Get tree of all objects, that should be notified (by source)
iterator = this.getPropagationTree(iterator, fieldContainer, evt, dirPort ,fieldContainer);
// Handle case : Infoshape is in interactor.
}else if(ISA(fieldContainer, 'core.gml:Interactor')){
iterator.addObject(fieldContainer);
}
}
*/
if(ISA(evt.child, 'core.gml:DataPort')){
// Get tree of all usages of the unit containing the changed Port
this.propagateByUsages( evt.child , evt );
}
this.propagateObjects(iterator, evt);
this.bPropagating = false;
COMMIT();
}
catch (e)
{
ROLLBACK();
this.bPropagating = false;
throw (e);
}
end
<@doc scope="private">
Propagete changes of a port to all usages of the containing diagram of this port
method propagateByUsages( port , evt )
if (!ISA(port , "core.gml:DataPort"))
throw new Error(-1 , "#TEXT[XMSG_EXPECTED_OBJ]") ;
var dir = ISA(port, 'core.gml:Outport') ?
#[WALK_BY_CONNECTABLE_TRG] :
#[WALK_BY_CONNECTABLE_SRC] ;
var mainI = $ENV.createObject("core.gml:ModelIterator");
mainI = this.getPropagationTree(mainI, port.parent, evt, #[WALK_BY_USAGE]);
var subI = $ENV.createObject("core.gml:ModelIterator");
var obj = null ;
while(obj = mainI.next()){
if(!ISA(obj, 'core.gml:Usage')){
throw new Error(-1 , "#TEXT[XMSG_OBJECT_MISMATCH]") ;
}
// Get tree of all objects, that should be notified (by target)
subI = this.getPropagationTree(subI, obj, evt, dir , port);
}
this.propagateObjects(subI, evt);
this.propagateObjects(mainI,evt);
end
<@doc scope="private" >
Get the field container.
method getFieldContainer(evt)
if (!ISA(evt.child , 'core.gml:Field' )) return null ;
var object = evt.child ;
var parent = object.parent || null ;
for ( var i = 0 ; i < 100 ; i++){
if ( ISA(parent , "core.gml:Configurable")){
return parent ;
}else{
object = parent ;
parent = object.parent || null ;
}
}
return null ;
/*
if (evt.type == 'onUpdateObject')
{
return evt.object && evt.object.parent && evt.object.parent.parent || null ;
}else
{
return evt.object && evt.object.parent || null ;
}*/
end
<@doc scope="private">
Propagates the change over tree of objects
method propagateObjects(iterator, evt)
if(!iterator) return;
var obj;
iterator.rewind(); // start from the beggining
var A = {};
while(obj = iterator.next())
{
if(!(obj.id in A) && obj.onPropagate ){
obj.onPropagate(evt);
A[obj.id] = true;
}
}
A=null;
end
<@doc scope="private">
Gets the tree of the objects that should be notified about the specific model change.
method getPropagationTree(iterator, obj, evt, walkMethod , rootPort)
var wFunc = null;
rootPort = (rootPort && ISA(rootPort, 'core.gml:Port'))? rootPort : null ;
var firstVisit = true;
switch (walkMethod)
{
case #[WALK_BY_USAGE]: wFunc = walkByUsages; break;
case #[WALK_BY_CONNECTABLE_SRC]: wFunc = walkByConnectableSrc; break;
case #[WALK_BY_CONNECTABLE_TRG]: wFunc = walkByConnectableTrg; break;
}
if(!iterator)
iterator = $ENV.createObject("core.gml:ModelIterator");
if(wFunc) iterator.search(obj, wFunc);
return iterator;
// - Walker callback for ModelIterator.
// - Collects all Usages of the specific Unit
function walkByUsages (obj, iterator)
{
if(!ISA(obj, 'core.gml:Unit')) return null;
var usages = obj.getUsages();
for( var i in usages)
{
iterator.addObject(usages[i]);
}
return null;
}
// - Walker callback for ModelIterator.
// - Iterates objects by Link Source (only 1st link)
function walkByConnectableSrc (obj, iterator)
{
var p;
if(ISA(obj, 'core.gml:Connectable'))
{
// Get all incoming links
var links = obj.getLinks(#[DIR_IN]), nextObj, nextPlug;
//var evtPort = (evt && ISA(evt.child, 'core.gml:Port')) ? evt.child : null;
for (l in links)
{
if(firstVisit && rootPort)
{ // check whether link is related to the modified element(Port)
p = links[l].target.getTarget();
if(p && (p.id != rootPort.id)) continue;
}
// add link to result iterator
iterator.addObject(links[l]);
// add connected object to result iterator, without continue the iteration
addLinkedObjToIterator(links[l], #[WALK_BY_CONNECTABLE_SRC], iterator);
}
firstVisit = false ;
}
else if (ISA(obj, 'core.gml:Link'))
{
// add connected object, without continue the iteration
addLinkedObjToIterator(obj, #[WALK_BY_CONNECTABLE_SRC], iterator);
}
return {};
}
// - Walker callback for ModelIterator.
// - Iterates objects by Link Target.
// - Each iteration puts all outcoming links and objects connected by them to the result iterator.
// - Callback returns a list of the object for next iterations which contains only Elements
// (but not Links) which infoshape is not self-contained.
function walkByConnectableTrg(obj, iterator)
{
var C={}, p, linkedObj;
if(ISA(obj, 'core.gml:Connectable'))
{
// Get all outcoming links
var links = obj.getLinks(#[DIR_OUT]), nextObj, nextPlug;
//var rootPort = (evt && ISA(evt.child, 'core.gml:Port')) ? evt.child : null;
//evtPort = (!evtPort && ISA(evt.object.parent , 'core.gml:Outport')) ? evt.object.parent : evtPort ;
for (l in links)
{ // check whether link is related to the modified element(Port)
if(firstVisit && rootPort)
{
p = links[l].source.getTarget();
if(p && (p.id != rootPort.id)) continue;
}
// add link to result iterator
iterator.addObject(links[l]);
// add connected object to result iterator and collect objects to continue the iteration
linkedObj = addLinkedObjToIterator(links[l], #[WALK_BY_CONNECTABLE_TRG], iterator);
if(linkedObj)
C[linkedObj.id] = linkedObj;
}
firstVisit = false;
}
else if (ISA(obj, 'core.gml:Link'))
{
// add connected object to result iterator (if needed), and collect objects to continue the iteration
linkedObj = addLinkedObjToIterator(obj, #[WALK_BY_CONNECTABLE_TRG], iterator);
if(linkedObj)
C[linkedObj.id] = linkedObj;
}
return C;
}
// - Local function
// - Adds linked (pointed by link) object to the iterator
// and returns this object for next iteration if it does not have self-contained infoshape
function addLinkedObjToIterator(link, dir, iterator)
{
if(!ISA(link, "core.gml:Link")) return null;
var nextItObj = null;
var plug = (dir == #[WALK_BY_CONNECTABLE_SRC]) ? link.source : link.target;
var port = (plug) ? plug.getTarget() : null;
var pInfoshape = (port) ? port.getInfoshape() : (plug && plug.parent && plug.parent.getInfoshape() || null);
if(pInfoshape && !pInfoshape.isBase())
{
// it is not self-contained infoshape, so continue the iteration
nextItObj = plug && plug.parent || null;
}
// add plug's parent to the iterator
if(plug)
iterator.addObject(plug.parent);
return nextItObj;
}