@doc hierarchy="GMLDOM" also="@Infoshape, @RefInfoshape">
The BOInfoshape is an element that represents a backend business object. It contains a collection of fields.
(c) SAP AG 2003-2006. All rights reserved.
#INCLUDE[svg:defs.inc]
Class BOInfoshape inherit Infoshape;
metadata title = '#TEXT[XTIT_BO_INFOSHAPE]';
metadata descr = '#TEXT[XTOL_BO_INFOSHAPE]';
//needed for Drag-and-Drop
metadata icon32 = '#URL[~res:skins.neutral.symbols.infoshape32.gif]';
//Temporary, for Drag-and-Drop
attach svg:Bipolar override {
geometry: {
resizeMode: #[SVG_KEEP_WIDTH],
rotateMode: #[SVG_ROTATE_FULL|SVG_FLIP_VBODY],
defWidth: 55,
defHeight: 40,
minWidth: 40,
minHeight: 30,
poleStart: 10,
poleEnd: 10,
padding: [0,10,0,10]
},
frameParts: [
//{type:'rect', x:'@x1', y:'@y1', width:'@w', height:'@h', fill:'@fillColor', stroke:'currentColor', 'stroke-width':2}
{type:'ellipse', cx:'0', cy:'0', rx:'@w/2', ry:'@h/2', fill:'@fillColor', stroke:'currentColor', 'stroke-width':2}
],
bodyParts: [
{type:'text', action:'rename', text:'@name', x:0, y:-18, 'font-size':'90%', fill:'@textColor', stroke:'none', 'text-anchor':'middle'}
]
}
<@doc>
A string that uniquely identify this BOInfoshape in the backend system it comes from. This is a string of an arbitrary format, that may be interpreted by the @BOInfoshapeIDParser! helper object.
The content and structure of the id may vary.
It is assumed that @BOInfoshape! objects are identical if their !bo_id strings are euqal. (case sensitive).
It is also assumed that for most purposes of the code it is not needed to parse these strings to their components, thus the parser is initialized on demand.
property bo_id = '';
metadata for bo_id = {
editor: 'str',
group: 'General',
visible: 'ISA(data, "core.gml:BOInfoshape") && GETVAR(\'showElementIds\')',
label: '#TEXT[XFLD_BO_UNIQUE_ID]',
priority: 500
}
<@doc scope="private">
A list of all @RefInfoshape! obejcts that point to the current BOInfoshape.
this is generated on load, and updated on the fly
virtual property refs_pointing_at_me = ^RefInfoshape[id];
<@doc scope="private">
Flag whether this BOInfoshape is under the process of batch insertion or removal of fields.
This is used to suppress individual infoshape notifiations.
See also methods insertFields_Batch() and removeFields_Batch()
virtual property inBatchOperation = false;
<@doc>
Gets the business object's @BOInfoshape!bo_id property - the business object's backend ID.
The object's unique ID
method getUniqueID()
return this.bo_id;
end
override method onCreate()
this.supercall();
end
override method notifyOnChange(changeType, changeData)
#TRACE[1, "BOInfoshape:notifyOnChange " + changeType];
var C = this.getAllRelatedRefInfoshapes();
for (var x in C)
{
var ref = C[x];
ref.notifyOnChange(changeType, changeData);
}
end
<@doc>
Retrieves a collection of all @RefInfoshape! objects that point to this BOInfoshape object.
Use this method instead of accessing the private member ~refs_pointing_at_me.
This private member is first calculated by scanning the @InfoshapePool!, and then by maintaining its consistency.
a collection of all @RefInfoshape! that reference this @BOInfoshape! object
method getAllRelatedRefInfoshapes()
//Patch for recalculation!!!
// This function is first called during onLoadproces, where index is not yet fully initialized,
// and then refs_pointing_at_me gets the value {} and never recalculated...
if (ISEMPTY(this.refs_pointing_at_me))
this.calculateRelatedRefs();
return this.refs_pointing_at_me;
end
<@doc scope="private">
Initializes the private member !refs_pointing_at_me, by scanning all existing @RefInfoshape! objects
method calculateRelatedRefs()
var unit = this.unit; //the BOInfoshapeUnit class.
if (!unit) return;
var usages = $ENV.organizer.getUsages(unit);
for (var x in usages) {
var usage = usages[x];
if (!usage || !usage.parent) continue;
if (usage.parent.isa('core.gml:RefInfoshape') && (usage.parent_prop == 'boInfoshape'))
{
this.refs_pointing_at_me[usage.parent.id] = usage.parent;
//this.addRefInfoshape(usage.parent);
}
}
end
<@doc scope="private">
remove a refInfoshape from internal list.
this method is used only by class RefInfoshape
method removeRefInfoshape(ref_infoshape)
delete this.getAllRelatedRefInfoshapes()[ref_infoshape.id];
end
<@doc scope="private">
add a refInfoshape to internal list.
this method is used only by class RefInfoshape
method addRefInfoshape(ref_infoshape)
this.refs_pointing_at_me[ref_infoshape.id] = ref_infoshape;
end
<@doc>
2 BO infoshapes are compatible if they have the same @BOInfoshape!bo_id property.
Another BO infoshape to be compared
Returns ~true if infoshapes are compatible; otherwise, returns ~false
override method isCompatible(other_infoshape)
if (!other_infoshape)
return false;
if (this.Class.fullname != other_infoshape.Class.fullname)
return false;
//all empty BO infoshapes are compatible...
if (this.isEmpty() && other_infoshape.isEmpty())
return true;
var errMsg = "#TEXT[XMSG_INVALID_BO_INFOSHAPE_ID]";
#DEVTIME[
if (!this.bo_id) throw new Error(-1, errMsg);
]
return this.bo_id == other_infoshape.bo_id; //case sensitive string comparison
end
method isEmpty()
return ISEMPTY(this.getFields());
end
///////////// Handle changes
override method onInsert(child, trans)
// If a floating object DO NOT NOTIFY
if(!this.unit) return;
//if field added, not part of batch operation - notify !
if (ISA(child, 'core.gml:Field') && !this.inBatchOperation)
{
this.notifyOnChange(#[INFOSHAPE_CHANGE_FIELD_ADD], {fields:[child]});
}
end
override method onRemove(child, trans)
// If a floating object DO NOT NOTIFY
if(!this.unit) return;
//if field removed, not part of batch operation - notify !
if (ISA(child, 'core.gml:Field') && !this.inBatchOperation)
{
this.notifyOnChange(#[INFOSHAPE_CHANGE_FIELD_DELETE], {fields:[child]});
}
end
override method onLoad()
this.supercall();
// This line is redundant, The calculation of the related infoshapes is done by using lazy loading.
//this.calculateRelatedRefs();
end
listen onUpdateObject for core.gml:Field
var infoshape = evt.object.parent;
if (ISA(infoshape, 'core.gml:BOInfoshape'))
infoshape.notifyOnChange(#[INFOSHAPE_CHANGE_FIELD_UPDATE], {field:evt.object, evt:evt});
end
<@doc>
Add an array of fields to the infoshape, and send a unified notification for all fields together.
Array o field objects to add to the infoshape
~true to suppress events on insertion of each field to the infoshape. ~false does not disable the events mechanism.
method insertFields_Batch(fields_array, suppress_events)
if (!fields_array.length || fields_array.length <= 0) return;
if (suppress_events == undefined) suppress_events = false;
try
{
this.inBatchOperation = true;
//suppress events
var old_suppress_events = $ENV.suppressEvents;
if (suppress_events)
{
$ENV.suppressEvents = true;
}
// insert all fields to the infoshape
for (var i = 0; i
Remove an array of fields from the infoshape, and send a unified notification for all fields together.
Array o field objects to remove from the infoshape
~true to suppress events on insertion of each field to the infoshape. ~false does not disable the events mechanism.
method removeFields_Batch(fields_array, suppress_events)
if (!fields_array.length || fields_array.length <= 0) return;
if (suppress_events == undefined) suppress_events = false;
try
{
this.inBatchOperation = true;
//suppress events
var old_suppress_events = $ENV.suppressEvents;
if (suppress_events)
{
$ENV.suppressEvents = true;
}
// insert all fields to the infoshape
for (var i = 0; i