<@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; }