@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