@doc hierarchy="GMLDOM">
Represents an element that owns a configuratino object
Child Elements
@gml:Infoshape, @gml:Configuration
(c) SAP AG 2003-2006. All rights reserved.
///////////////////////////////////////////////////////////////////////
//
Class Configurable inherit Connectable;
implement dev:IInfoset;
metadata title = '#TEXT[XTIT_CONFIGURABLE]';
metadata descr = '#TEXT[XTOL_CONFIGURABLE]';
metadata configurationType = 'core.gml:Configuration';
///////////////////////////////////////////////////////////////////////
// VIRTUAL/STATIC PROPERTIES
<@doc>The configuration or infoshape that belong to this element. Use @getConfiguration or @getInfoshape to obtain this member.
//strong readonly property configuration = ^Configuration;
readonly property configuration = ^Configuration; //not strong, but a pointer to an infoshape stored at the pool
//////////////////////////////////////////////////////////////////////
// PROPERTIES
///////////////////////////////////////////////////////////////////////
// METHODS
<@doc>
Gets the configuration related to the object
The object's configuration
method getConfiguration()
return this.getProperty('configuration');
end
/////////////////////////////////////
// IINFOSET METHODS - prototype implementation
<@doc>
Gets the infoset's id
The infoset's id
override method getInfosetId()
return this.id;
end
<@doc>
Gets the infoset's friendly name
The infoset's friendly name (alaias)
override method getInfosetAlias()
return this.name;
end
<@doc>
Gets the @gml:RefInfoshape! related to the object
The object's @gml:RefInfoshape!, or null if no infoshape is available
override method getInfoshape()
var c = this.getConfiguration();
return c && c.isa('core.gml:Infoshape') ? c : null;
end
///////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
override method onCreate()
this.supercall();
this.createInfoshape(null);
end
override method onInsertMe()
this.supercall();
this.createInfoshape(null);
end
override method onLoad()
this.supercall();
var ref_infoshape = this.getInfoshape(); //this will resolve the ref pointer.
if (ref_infoshape)
{
//debugger;
//ref_infoshape.elements_using_me[this.id] = this;
ref_infoshape.registerElement(this);
}
/*
var ref = this.configuration;
if (!ref) return;
if (typeof ref == "string") {
//Possibly patchy - need reevaluation later...
//ref = $ENV.model.infoshape_pool.getInfoshape(ref);
ref = $ENV.model.getElement(ref); //this is full ID, so use appropriate method
if (!ref) {
return;
}
this.setInfoshape(ref);
}
*/
end
override method onRemoveMe(parnet, trans){
this.supercall();
if (this.configuration && this.configuration.unregisterElement)
this.configuration.unregisterElement(this);
}
<@doc scope="private">
method setInfoshape(ref_infoshape)
try{
BEGIN('Setting Infoshape');
var old_id = this.configuration && this.configuration.id || null;
// Unregister the old refInfoshape/
if ( this.configuration && this.configuration.unregisterElement ) this.configuration.unregisterElement(this);
this.configuration = ref_infoshape; //don't use insertElement because this is not strong property
// Register the new refInfoshape.
if ( ref_infoshape && ref_infoshape.registerElement) ref_infoshape.registerElement(this);
this.notifyOnInfoshapeChange(#[INFOSHAPE_CHANGE_REF_REPLACED], {old_id:old_id, new_id:ref_infoshape.id});
COMMIT();
}catch(e){
var msg = "Error in setInfoshape " + (ref_infoshape && ref_infoshape.id || '') + " in element " + this.id + " desc: " + e.description ;
#TRACE[4, msg ];
ROLLBACK();
throw new Error(-1, msg );
}
end
<@doc scope="private">
method clearInfoshape()
if (this.configuration)
{
var old_id = this.configuration.id;
ref_infoshape.unregisterElement(this);
this.configuration = null; //don't use removeElement because this is not strong property
this.notifyOnInfoshapeChange(#[INFOSHAPE_CHANGE_REF_REPLACED], {old_id:old_id, new_id:null})
}
else
{
// Is this needed??
this.configuration = null; //don't use removeElement because this is not strong property
}
end
<@doc>
Creates or modifies the @gml:RefInfoshape! for this @gml:Configurable! object. Use this method as the way to modify the infoshape related to this object.
When ~null is passed as parameter, this method creates a new @gml:RefInfoshape! to this object
When @gml:RefInfoshape! is passed as parameter, this method sets this object's infoshape to be the parameter @gml:RefInfoshape!.
method createInfoshape(other_infoshape);
if (!other_infoshape) {
//create ref infoshape pointing to none\
var traceMsg = "#TEXT[XMSG_CREATE_REF_INFOSHAPE]";
#TRACE[1, traceMsg];
var ref_infoshape = $ENV.model.createElement('core.gml:RefInfoshape');
if (!ref_infoshape) {
throw new Error(-1, "createInfoshape: #TEXT[XMSG_FAIL_CREATE_REF_INFOSHAPE]");
}
/*
var config_class = this.getConfigurationClassName();
if (config_class)
{
//create a temporary, empty infoshape...
var bo_infoshape = $ENV.model.createElement(config_class);
ref_infoshape.setBOInfoshape(bo_infoshape);
this.clearInfoshape();
this.setInfoshape(ref_infoshape);
$ENV.model.infoshape_pool.storeInfoshape(ref_infoshape);
$ENV.model.infoshape_pool.storeInfoshape(bo_infoshape);
}
*/
//this.clearInfoshape();
this.setInfoshape(ref_infoshape);
$ENV.model.infoshape_pool.storeInfoshape(ref_infoshape);
return;
}
/*
if (other_infoshape.isa('gml:BOInfoshape'))
{
//create a ref infoshape that points to that BO infoshape
#TRACE[1, "Create ref infoshape pointing to another BOInfoshape"];
var ref_infoshape = $ENV.model.createElement('core.gml:RefInfoshape');
ref_infoshape.setBOInfoshape(other_infoshape);
if (!ref_infoshape) {
throw new Error(-1, "createInfoshape: Empty infoshape created (2)");
}
this.clearInfoshape();
this.setInfoshape(ref_infoshape);
$ENV.model.infoshape_pool.storeInfoshape(ref_infoshape);
return;
}
*/
//relevant for interactors...
if (other_infoshape.isa('gml:RefInfoshape'))
{
//use same ref infoshape
var traceMsg = '#TEXT[XMSG_USING_EXIST_REF_INFOSHAPE]';
#TRACE[1, traceMsg];
//this.clearInfoshape();
this.setInfoshape(other_infoshape);
return;
}
return;
end
<@method name="getConfigurationClassName">
Returns the class name of the configuration object that is created with the creation of this element.
The default implementation returns the class name that appears in the ~configurationType metadata.
Override this method to provide another (more dynamic) way to decide on the configuration class name.
Note that currently all elements that inherit from @gml:Configurable currently hold an @gml:Infoshape and not a @gml:Configuration.
The class name of a subclass of @gml:Configuration that should be created for this component, or ~'' to create no configuration.
method getConfigurationClassName()
return this.Class.metadata.configurationType || '';
end
<@doc>
A callback method that is called if this object's @gml:RefInfoshape! is modified, or the @gml:BOInfoshape object it points to is modified.
the type of change of infoshape
An object containing data related to the change in the infoshape
See more details on the parameters passed to this method in @gml:Infoshape!notifyOnChange.
Override this method to handle changes in this object as a result of changes in the related infoshape.
method notifyOnInfoshapeChange(changeType, changeData)
#TRACE[1, "Element " + this.id + ": notifyOnInfoshapeChange " + changeType];
var in_links = this.getLinks(#[DIR_IN]);
for (var k in in_links)
in_links[k].notifyOnInfoshapeChange(changeType, changeData);
var out_links = this.getLinks(#[DIR_OUT]);
for (var k in out_links)
out_links[k].notifyOnInfoshapeChange(changeType, changeData);
end
// Queries whether a given source plug can be connected to a target plug under this element.
// It allows only one out plug to be connected to this element's in plug.
<@doc>
Queries whether a given source plug can be connected to a target plug under this element
The source plug (belonging to another element)
The target plug (belonging to this element)
A whitespace-separated list of all valid link types that can be connected between the source and target plugs, or ~null in case the source and target plugs cannot be connected.
uses methods canConnectDataPlugs and canConnectDirectPlugs.
The link type created determines the manipulation on the target element's infoshape.
override method canConnect(srcPlug, trgPlug)
if (!srcPlug || srcPlug.parent == this) return '';
if (!trgPlug || trgPlug.parent != this) return '';
if ((srcPlug.dir == trgPlug.dir) && (srcPlug.dir != #[DIR_IO])) return '';
//=== Navigation Links ===
if (trgPlug.isa('core.gml:DirectPlug') && srcPlug.isa('core.gml:DirectPlug'))
{
return this.canConnectDirectPlugs(srcPlug, trgPlug);
}
//=== Data or bind links ===
if (srcPlug.isa('core.gml:Outplug') && trgPlug.isa('core.gml:Inplug'))
{
return this.canConnectDataPlugs(srcPlug, trgPlug);
}
return '';
end
<@doc>
returns an array representing all the events published or consumed by this element
Specify incoming or outgoing events
An array of event names
Override this method to expose the element's incoming and outgoing events.
method getEvents(dir)
return [];
end
<@doc>
Queries whether a given source plug can be connected to a target plug under this element
The source plug (belonging to another element)
The target plug (belonging to this element)
A whitespace-separated list of all valid link types that can be connected between the source and target plugs, or ~null in case the source and target plugs cannot be connected.
method canConnectDirectPlugs(srcPlug, trgPlug)
var linkslist = 'core.gml:NavigationLink';
if (!trgPlug.parent.isa('core.gml:EventPort') && !srcPlug.parent.isa('core.gml:EventPort')){
return linkslist;
}
if (trgPlug.parent.isa('core.gml:EventInport'))
return '';
if (srcPlug.parent.isa('core.gml:EventOutport'))
return '';
if (trgPlug.parent.isa('core.gml:EventOutport')) {
if (srcPlug.parent.isa('core.gml:EventInport'))
return '';
return linkslist;
}
if (srcPlug.parent.isa('core.gml:EventInport')) {
return linkslist;
}
return linkslist;
function inArray(arr, obj) {
if (!arr) return -1;
if (typeof arr != 'object') return -1;
if (!arr.length) return -1;
for (var i = 0; i < arr.length; ++i) {
if (arr[i] == obj)
return i;
}
return -1;
}
end
<@doc>
Queries whether a given source plug can be connected to a target plug under this element
The source plug (belonging to another element)
The target plug (belonging to this element)
A whitespace-separated list of all valid link types that can be connected between the source and target plugs, or ~null in case the source and target plugs cannot be connected.
method canConnectDataPlugs(srcPlug, trgPlug)
var linkslist = '';
//temporary...
//linkslist += ' core.gml:BindLink core.gml:DataLink';
var target_el = trgPlug.getRelatedObject() || null;
var source_el = srcPlug.getRelatedObject() || null;
if (!source_el || !target_el)
return;
var errMsg = "#TEXT[XMSG_LINK_ERR]";
#DEVTIME[
if (!target_el.isa('core.gml:Configurable') || !source_el.isa('core.gml:Configurable'))
throw new Error (-1, errMsg);
]
// BindLink logic
if (target_el.isa('core.gml:Interactor'))
{
//target element gets the same infoshape as the source element
var enm = this.countIncomingLinks(trgPlug);
if (enm == 0)
linkslist += ' core.gml:BindLink';
}
//VC Core port logic
else if (target_el.isa('core.gml:DataPort'))
{
var src_is = source_el.getInfoshape();
var trg_is = target_el.getInfoshape();
if (trg_is && trg_is.isCompatible(src_is))
{
var enm = this.countIncomingLinks(trgPlug);
switch (enm) {
case -1:
break; //Bind link exists - do not allow more links
case 0:
linkslist += ' core.gml:DataLink core.gml:BindLink';
break;
case 1:
linkslist += ' core.gml:DataLink';
break;
}
}
else
{
//Nothing to do, ref and BO infoshapes do not change
linkslist += ' core.gml:DataLink';
}
}
return linkslist;
end
<@doc scope="private">
count incoming links that go into a given inplug.
return
0 - no incoming links
1- 1 or more data links
-1 - 1 BindLink (more is not allowed...)
method countIncomingLinks(trgPlug)
#DEVTIME[
if (trgPlug.parent != this)
throw new Error(-1, "countIncomingLinks - plug " + trgPlug.id + " does not below to element " + this.id);
]
// if infoshapes are compatible - allow one bind link creation
var links = this.getLinks(#[DIR_IN]);
var incoming_link = null;
for (var k in links)
if (links[k].target.id == trgPlug.id)
{
incoming_link = links[k];
break; //one bind link already exists
}
if (!incoming_link) //no incoming links - keep your options!
return 0;
else if (incoming_link.isa('core.gml:DataLink')) //incoming link is DataLink - allow more DataLinks
return 1;
else //incoming link is bind - do not allow more links
return -1;
end
///////////////////////////////////////////////////////////////////////
// RULES
/*Configure RULES
$ENV.extendRule('definePropertyGroups', {
constraint:'(object isa gml:Configurable)',
behavior: function() {
groups.push({id: 'Appearance', type:'group', title:'Appearance', toggle:'off', inner:4, priority:300,
visible: 'ISA(data, "core.gml:ScenarioUsage", "core.gml:Interactor")'});
},
comments:'⇒ [Appearance]'
});
*/