<@doc alias="pool" hierarchy="GMLDOM"> The InfoshapePool is the unit that stores all infoshapes in the model. The infoshape pool stores all @RefInfoshape! objects in the model. In addition, it stoers usages to all @BOInfoshapeUnit! units. Infohsape pool also handles notification on changes in infoshapes in the model. (c) SAP AG 2003-2006. All rights reserved. #ALIAS[g=core.svg:GmlDrawing] #INCLUDE[svg:defs.inc] Class InfoshapePool inherit Unit; metadata title = '#TEXT[XTIT_INFOSHAPE_POOL]'; metadata descr = '#TEXT[XTOL_INFOSHAPE_POOL]'; metadata icon16 = '#URL[~res:skins.neutral.symbols.module16.gif]'; metadata icon32 = '#URL[~res:skins.neutral.symbols.module32.gif]'; //metadata usageElement = ''; //no usage is allowed for this unit ??? metadata childElements = ['core.gml:RefInfoshape', 'core.gml:BOInfoshapeUsage']; <@doc> Collection for optimizations: All @gml:BOInfoshapeUsage objects stored in this unit, indexed by the unique id. (@BOInfoshape!bo_id);Actor virtual property BOInfoshapeMap = ^core.gml:BOInfoshapeUsage[id]; property systemName = ''; /////////////////////////////////////////////////////////////////////// // PROPERTIES <@doc> Gets a global model @BOInfoshape! by its @BOInfoshape!bo_id (backend unique id) The backend ID of the requested @BOInfoshape! object The @BOInfoshape! that was found, or ~null method getBOInfoshape(bo_id) var infoshape_usage = this.BOInfoshapeMap[bo_id]; if (!infoshape_usage) return null; var unit = infoshape_usage.getTarget(); if (!unit) return null; var bo_inf = unit.getBOInfoshapeElement(); if (!bo_inf) { var errMsg = "#TEXT[XMSG_ERROR]" + ": " + "#TEXT[XMSG_FAILED_TO_FIND_INFOSHAPE] " + bo_id+ " in memory"; #LOG[1, errMsg]; } return bo_inf; end <@doc> Gets a @RefInfoshape! object, using its GML ID The GML ID of the requested @RefInfoshape! object The @RefInfoshape! that was found method getRefInfoshape(infoshape_id) return this.elements[infoshape_id] || null; end <@doc> store an infoshape in the infoshape pool. Can store @RefInfoshape! object The @RefInfoshape! to store in the infoshape pool method storeInfoshape(infoshape_obj) //TODO: replace with calling a service this.insertElement(infoshape_obj, 'elements'); end <@doc> create new @BOInfoshapeUnit! and inside it a @BOInfoshape! object with a given backend ID. Put a usage for the created unit in the infoshape pool. Note: DEPRICATED.This function returns ~null if the backend ID of the created infoshape already exists. The class name for the @BOInfoshape! object that is created inside the @BOInfoshapeUnit! unit. The name for the created unit and its usgae The Backend ID for the created @BOInfoshape! The created infoshape unit, or ~null if this @BOInfoshape! already exists. method createNewBOInfoshapeUnit(bo_infoshape_class, bo_name, bo_id) return this.createBOInfoshapeUnit(bo_infoshape_class, bo_name, bo_id); end <@doc> create new @BOInfoshapeUnit! and inside it a @BOInfoshape! object with a given backend ID. Put a usage for the created unit in the infoshape pool. Note: This function returns ~null if the backend ID of the created infoshape already exists. create new @BOInfoshapeUnit! and inside it a @BOInfoshape! object with a given backend ID. Put a usage for the created unit in the infoshape pool. The class name for the @BOInfoshape! object that is created inside the @BOInfoshapeUnit! unit. The name for the created unit and its usgae The Backend ID for the created @BOInfoshape! create new @BOInfoshapeUnit! and insert the @BOInfoshape! object into it with a given backend ID. Put a usage for the created unit in the infoshape pool. The @BOInfoshape! object that will be inserted into the @BOInfoshapeUnit! unit. The name for the created unit and its usgae The Backend ID for the created @BOInfoshape! The created infoshape unit, or ~null if this @BOInfoshape! already exists. method createBOInfoshapeUnit(aBOInfoshape, aBOName, aBOId) var boInfoshape = null; var boName = aBOName; var boId = aBOId; if (typeof aBOInfoshape == 'object'){ boInfoshape = aBOInfoshape ; if(boName){ boInfoshape.setProperty('name', boName); }else{ boName = boInfoshape.getProperty('name'); } if(boId) { boInfoshape.setProperty('bo_id', boId); }else{ boId = boInfoshape.getProperty('bo_id'); } } //some validation tests #DEVTIME[ if (!boName) { throw new Error (-1, "createNewBOInfoshapeUnit: must specify aBOName parameter"); return null; } if (!boId) { throw new Error (-1, "createNewBOInfoshapeUnit: must specify aBOId parameter"); return null; } ] if (this.getBOInfoshape(boId)) { //a unit with this BO ID already exist, return a usage to the existing unit return null; /* var infoshape_usage = this.BOInfoshapeMap[bo_id]; var bo_unit = infoshape_usage.getTarget(); return bo_unit; /// */ } //else - no such BO found !! var usage = this.createUnit('gml:BOInfoshapeUnit', {name:boName}, {name:boName,boID:boId}); if (usage) { var unit = usage.getTarget(); if (!boInfoshape){ unit.createElement(aBOInfoshape, 'elements', {name:boName, bo_id:boId}); }else { unit.insertElement(boInfoshape, 'elements'); } //Patch: re-call onInsert, because when onInsert was called for the usage, // the unit did not have a related BOInfoshape. // and thus the internal map was not updated.. this.onInsert(usage); //$ENV.organizer.makeUnitReusable(unit); return unit; } return null; end <@doc> Cleans the entire infoshape pool - should be used only when a floorplan is deleted method cleanAll() for (var i in this.elements) { var ref = this.elements[i]; this.removeElement(ref,"elements"); } end <@doc scope="private"> This method goes over all RefInfoshapes and tests whether they are unused. It removes all unused RefInfoshapes. Unused BOInfoshapes are not tested and are not removed! This method is called when the model is saved, from inside the updateIndex method. Index object from ModelOrganizer is passed as parameter (it is not modified inside the method. Algorithm: go over all Refs. For each ref - if it has elements, - if one of these elements belong to a used unit - mark RefInfoshape as used - otherwise, mark Ref for deletion - if it does not have elements, mark Ref for deletion (don't mark BO) return true - if any ref removed, false otherwise ModelOrganizer.index - not modified inside this method ~true if any @RefInfoshape! was deleted. ~false otherwise. method cleanup(index_object) var ret = false; for (var i in this.elements) { try { var ref = this.elements[i]; if (ref.isa('core.gml:BOInfoshapeUsage')) continue; if (!ref.isa('core.gml:RefInfoshape')) { #LOG[4, "Unsuported element type " + ref.Class.fullname + " in Infoshape Pool"]; continue; } //ref is RefInfoshape var bo_unit = (ref.boInfoshape && ref.boInfoshape.getTarget()) || null; var remove_this_ref = false; var C = ref.getElementsUsingMe(); if (ISEMPTY(C)) { //no model element uses this Ref. Mark it for deletion. remove_this_ref = true; } else { //go over all elements that point to this Ref. See that at least one belongs to a unit in use var some_elements_in_use = false; for (var j in C) { var elem_unit = C[j].unit && C[j].unit.id || null; if (elem_unit && !index_object[elem_unit].unused) { some_elements_in_use = true; break; } } //ref is indeed used by other elements if (!some_elements_in_use) remove_this_ref = true; } if (remove_this_ref) { //ref is not used by anyone.. this.removeRefInfoshape(ref);ret = true; } } catch(e) { //#DEVTIME[ debugger; ]; #LOG[1, "Error in infoshape pool cleanup: " + e.description]; } } return ret; end <@doc> Remove a refInfoshape from the infoshape pool. method removeRefInfoshape(ref) if (!ref) return; try { BEGIN(); if (ref.boInfoshape) ref.getBOInfoshape().removeRefInfoshape(ref); this.removeElement(ref.id, 'elements'); COMMIT(); } catch(e) { #LOG[1, "Error in infoshape pool cleanup. Failed to remove RefInfoshape " + ref.id + ':' + e.description]; ROLLBACK(); } end override method onLoad() //fill the map with all the objects. this.supercall(); //go over all BOinfoshapeUsage and store them in map //later - map all additional BO usages in this map. var elements = this.getCollection('core.gml:BOInfoshapeUsage', true) for (var i in elements) { var child = elements[i]; this.BOInfoshapeMap[child.getUniqueID()] = child; } end override method onInsert(child, trans) this.supercall(); if (child.isa('core.gml:BOInfoshapeUsage')) { var unit = child.getTarget(); if (unit) this.BOInfoshapeMap[unit.getUniqueID()] = child; } end override method onRemove(child, trans) this.supercall(); if (child.isa('core.gml:BOInfoshapeUsage')) { var unit = child.getTarget(); if (unit) delete this.BOInfoshapeMap[unit.getUniqueID()]; } end override method canActivate() //PROMPT('Cannot drill into this pattern...'); return false; end <@doc> Delete a @BOInfoshapeUnit This method first checks if this BOInfoshape is in use by and elements. Then it deletes all RefInfoshapes (that contain BOInfoshapeUsage) to this BOInfoshapeUnit in order to delete it. It also deletes the BOInfoshapeUsage in the infoshape pool. The unit to delete true if succeeded in deleting, false if failed for some reason. method deleteBOInfoshapeUnit(bo_unit, force) //get the BO if (!bo_unit) return false; var bo = bo_unit.getBOInfoshapeElement(); if (!bo) return false; //get all refs var all_refs = bo.getAllRelatedRefInfoshapes(); var used = false; if(!force){ //check if it is used for (n in all_refs) { var ref = all_refs[n]; var A = SPLIT(TRIM(ref.unitsUsingMe),','); if(A) { used = true; //TODO: if unit is really loaded, also print the element names ??? otherwise write only unit name #TRACE[2, "Deleteing BOInfoshapeUnit " + bo.getUniqueID() + ": related RefInfoshape " + ref.id + " is used by units " + ref.unitsUsingMe]; } } if (used) { #TRACE[2, "Please check source viewer to see details on unit content"]; return false; } } //delete all refs if this BO is not used // delete all refs, which have no elements for them for (n in all_refs) { var ref = all_refs[n]; try{ ref.parent.removeElement(ref, ref.parent_prop); //generic code for deleting refInfoshapes }catch(e){ #TRACE[4, "deleteBOInfoshapeUnit:Usage failed to be removed. UsageId:" + n ]; //debugger; } } // delete usage from infoshape pool, that also updates the internal map var p_usages = $ENV.model.organizer.getPrimaryUsages(bo_unit); var primaryDeletedCount = 0; for (i in p_usages) { var usage = p_usages[i]; if(!usage) { #TRACE[4, "deleteBOInfoshapeUnit:Usage found to be null, should check why this is happening. UsageId:" + i ]; continue; } if (usage.parent === this) { //this is the Usage in the pool - delete it this.removeElement(usage, 'elements'); primaryDeletedCount++; } } // delete the entry from BOInfoshapeMap delete this.BOInfoshapeMap[bo_unit.getUniqueID()]; //from here - this is only validation code if (primaryDeletedCount > 1) #TRACE[1, "deleteBOInfoshapeUnit: deleted more than one primary usage"]; //check in index that no usages remain, var usages = $ENV.model.organizer.getUsages(bo_unit); for (x in usages) { #TRACE[1, "deleteBOInfoshapeUnit: usages found for BO unit, even after cleanup"]; return false; } return true; end /* assumptions: we update all core structures, then patterns kit need to do their adjustments */ method renameBOID(old_boid, new_boid) var bo_inf = this.getBOInfoshape(old_boid); if (!bo_inf) { #TRACE[2, "Trying to rename infoshape " + old_boid + ". Infoshape not found"]; return false; } if (!new_boid) { #TRACE[2, "Trying to rename infoshape " + old_boid + ". The new infoshape name is invalid"]; return false; } var bo_inf2 = this.getBOInfoshape(new_boid); if (bo_inf2) { #TRACE[2, "Trying to rename infoshape " + old_boid + ". Another infoshape with that name already exists (" + bo_inf2.id + ")"]; return false; } //go over all usages and replace the id in the member var all_usages = $ENV.model.organizer.getUsages(bo_inf.unit); for (var i in all_usages) { var usage = all_usages[i]; if (usage==null|| !usage.boID) continue; if (usage.boID == usage.name) usage.setProperty('name', new_boid); usage.setProperty('boID', new_boid); } //update the internal cache. var usage = this.BOInfoshapeMap[old_boid]; delete this.BOInfoshapeMap[old_boid]; this.BOInfoshapeMap[new_boid] = usage; if (bo_inf.name == old_boid) bo_inf.setProperty('name', new_boid); bo_inf.setProperty('bo_id', new_boid); end /* Sample for deleting duplicate BOs method deleteDuplicateBOUsages(){ var usages = this.getCollection('gml:BOInfoshapeUsage'); var map = {}; var str = ""; for (var id in usages){ var uID =usages[id].getUniqueID(); if(!uID)continue; if(!map[uID] ) { map[uID] = []; var bo = this.getBOInfoshape(uID); if ( !bo) continue; str+=bo.getProperty("name") +" used by more then one element
"; } map[uID].push(usages[id]); } var res = CONFIRM('#TEXT[XMSG_DUPLICATE_INFOSHAPE]','Yes No',str,true); if (res==1) { this.cleanup( this.model.organizer.index); var counter = 0; for (var id in map){ if(map[id].length > 1){ for ( var i=1; i< map[id].length;i++){ // Don't delete the boinfoshapeusage that in the BOInfoshapeMap. this.removeElement(map[id][i],'elements'); counter++; } } } } CONFIRM(counter + ' Usages have been deleted.','Yes No',"",true); } */ <@doc> Clone @BOInfoshapeUnit! with a given backend ID. Note: This function returns ~null if the backend ID of the cloned infoshape already exists. The @core.gml:BOInfoshapeUnit! to clone The Backend ID for the cloned @BOInfoshape! The created infoshape unit, or ~null if this @BOInfoshape! already exists. method cloneBOInfoshapeUnit(bou , newBOId) //some validation tests #DEVTIME[ if (!bou) { throw new Error (-1, "cloneBOUnit: must specify BOInfoshapeUnit"); return null; } if(!newBOId) { throw new Error (-1, "cloneBOUnit: must specify new bo_id"); return null; } ] var newBou = bou.clone(); if (!newBou) return null; var newBo = newBou.getBOInfoshapeElement(); if(!newBo) return null; newBo.setProperty('bo_id',newBOId ); var usage = newBou.createNewUsage({'boID':newBOId}); if (usage){ if (!this.insertElement(usage, 'elements')) return null; return newBou; } return null; end