@doc alias="obj" hierarchy="GMLDOM">
Transactional implementation of GmlObject.
Instances of TransObject or any of its derived classes are persistent objects that are managed using transactions.
To create instances of TransObject or any of its sub-classes, use respective GMLDOM methods.
Do not use constructors for initializing instances of TransObject.
(c) SAP AG 2003-2006. All rights reserved.
///////////////////////////////////////////////////////////////////////
// CLASS HEADER
#INCLUDE[gml:defs.inc]
Class TransObject inherit GmlObject;
metadata title = 'Transactional Object';
metadata descr = 'Base class for all transactional model entities';
///////////////////////////////////////////////////////////////////////
// PROPERTIES
<@doc group="I. Properties">Gets or sets the object notes
property notes = '';
metadata for notes = {
editor: 'text',
group: 'General',
priority: 300,
label: '#TEXT[XTIT_COMMENTS]',
height: 75
}
<@doc>Gets or sets the stereotype tags
property tags = '';
<@doc>Gets or sets the role of object in the current unit
property roleName = '';
<@doc scope="private">Gets the child elements collection
//virtual readonly property children = null; // in fact the type is ^Element[id]
virtual readonly property children = ^Element[id];
<@doc>Gets or sets the property name at the parent object
virtual readonly property parent_prop = '';
override metadata for id = {
editor: 'str',
group: 'General',
priority:100,
label: '#TEXT[XTIT_ELEMENT_ID]',
visible: 'GETVAR(\'showElementIds\')',
active: false
}
override metadata for name = {
editor: 'str',
group: 'General',
priority: 200,
label: '#TEXT[XFLD_NAME]'
}
// Internal for managing internal property tranlations:
virtual property translationKeyMap = {};
///////////////////////////////////////////////////////////////////////
// METHODS
<@doc group="II. Methods">
Tests whether this object contains a given child object
The child object to test
The test result
override method contains(child)
for (var e=child; e && e.parent && e.parent != this; e=e.parent);
return e ? true : false;
end
<@doc>
Creates a new element and inserts it under this object
The element class name
The property name into which this child will be assigned
A collection of property values for initializing the new element
Integer that specifies the placement of the new element.
If this parameter is specified, the child element will be inserted immediately before the specified index.
This parameter is only relevent to properties of type vector
The newly created element, or ~null in case of any error
This method is sealed and cannot be overridden on sub-classes. If needed, you can override the @gml:GmlObject!onCreate callback method to extend the creation operation.
sealed method createElement(className, prop_name, values,before)
#DEVTIME[ //BC check
if (prop_name == undefined || typeof prop_name != 'string')
throw new Error(-1, "Error in createElement: prop_name argument is invalid");
]
if (!values) values = {};
values.id = $ENV.model.newId();
var elem = $ENV.createObject(className, values);
if (!elem || !this.insertElement(elem, prop_name ,before)) return null;
return elem;
end
<@doc>
Gets a collection of child elements by a specified type.
The class of elements to get
Indicates whether to perform a deep search
The requested child elements collection
By default this method will search only the immediate child elements. To search
all descendant elements, regardless of their nesting depth, set the ~deep parameter to ~true.
method getCollection(className, deep)
var E, C=(this.children) ? this.children : {};
if (arguments.length == 3) {
E=arguments[2];
} else {
E={};
className=$DOM.toQName(className);
if (!className || !C) return E;
}
for (var k in C) {
if (className in C[k].Class.precedents) E[k]=C[k];
if (deep && C[k].children) C[k].getCollection(className, true, E);
}
return E;
end
<@doc>
Gets a child element by Id
The Id of the child element to get
Indicates whether to perform a deep search
The requested child element
By default this method will search only the immediate child elements. To search
all descendant elements, regardless of their nesting depth, set the ~deep parameter to ~true.
method getElement(id, deep)
var C=(this.children) ? this.children : {};
var elem=C && C[id] || null;
if (elem || !deep || !C) return elem;
for (var k in C) {
if (!C[k].children) continue;
var elem=C[k].getElement(id, true);
if (elem) return elem;
}
return null;
end
override method getProperty(name)
if (arguments.length == 2) {
name = $DOM.getAspect$Name(arguments[0], arguments[1]);
}
var prop=this.Class.persistent[name];
if (this.hasOwnProperty(name))
{
if (prop && (prop.type.charAt(0) == 'p') && (typeof this[name] == 'string'))
{
//check that indeed this is a full ID - and load it
this[name] = $ENV.model.getElement(this[name]);
}
////// TRANSLATION SUPPORT ////////
if (#SYS[SUPPORT_TRANSLATION] && prop && prop.metadata && prop.metadata.translation &&
prop.metadata.translation.translate && this[name])
{
var key = this[name];
var value = this.getTranslation(key);
if(!value)
{
// No translation found for the property.
var newValue = TRIM(this[name]);
if(!prop.exprType || (prop.exprType == 'String' && newValue && newValue.charAt(0)!= '='))
{
try
{
//Insert it to the translation table:
key = this.insertTranslation(prop.metadata.translation.type, newValue);
if(key)
{
// Set the new property and its key internally:
this.setTransKey( name, key);
}
// Setting the property:
value = this[name];
this[name] = key;
}
catch(e)
{
// Could not insert error:
PROMPT(e.description + '- See property \'' + name + '\'');
return this[name];
}
}
else
{
return this[name];
}
}
else
{
// Translation found, update the internal object mapping:
if(!prop.exprType || (prop.exprType == 'String' && value.charAt(0)!= '='))
{
key = this.getTransKey(name);
if(!key && value)
{
this.setTransKey( name, this[name])
}
}
}
return value;
}
////// END TRANSLATION////////
return this[name];
}
var P=this.Class.properties[name];
return (P && P.isProperty) ? P.value : null;
end
sealed method setProperty(name, value, silent)
var C=this.Class, P=C.properties[name];
var transType = null;
if (!P || P.isReadonly) return false;
if (P.isVirtual) {
if (value === undefined) delete this[name]; else this[name] = value;
return true;
}
var oldval = P.isStatic ? P.value : this.hasOwnProperty(name) ? this[name] : void(0);
if (oldval === value) return true;
var msg1 = '#TEXT[XMSG_FAIL_TYPE_CHECK_SETPROPRTY]';
var msg2 = 'when expecting type';
#DEVTIME[
if (P && P.typecheck && (typeof value == 'object') && !ISA(value, P.typecheck))
throw new Error(-1, msg1 +" " + value.Class.fullname + " "+ msg2+ " " + P.typecheck);
]
////// TRANSLATION SUPPORT ////////
if (#SYS[SUPPORT_TRANSLATION])
{
// Prapare the translation. The actual action will take place in the Transaction phaze.
if(P.metadata && P.metadata.translation && P.metadata.translation.translate // make sure it is translatable
&& typeof value == 'string')
{
if(!P.exprType || (P.exprType == 'String' && value.charAt(0)!= '='))
{
oldval = this.getTranslation(this[name]);
if(!oldval)
{
oldval = this[name];
}
transType = P.metadata.translation.type;
}
}
}
////// END TRANSLATION////////
if (P.isStatic) {
//TODO: place this in a transaction ??
var oldval = P.value ;
C.prototype[name] = value;
P.value = value;
var evt = document.createEventObject();
evt.type = 'onUpdateObject';
evt.object = this;
evt.name = name;
evt.value = value;
evt.oldval = oldval;
$ENV.fireModelEvent(evt);
return true;
}
if (silent) {
if (value === undefined) delete this[name]; else this[name] = value;
// setDirty needsto know if the change was only on the SVG level - see model.gs
var isSVGChange=false;
if (this.Class.properties[name].Class.isAspect && "svg" == this.Class.properties[name].Class.prefix)
isSVGChange=true;
this.setDirty(isSVGChange);
return true
}
var trans = $ENV.recorder.execute('doSetProperty', this, name, value, oldval, transType);
return trans ? true : false;
end
<@doc scope=private>A helper method for assigning a value to a strong property.
Only use this method from inside ~insertElement and other core functions !
sealed method assignChild(child, prop_name, before)
var p = this.Class.properties[prop_name];
if (!p) {
throw new Error(-1, "#TEXT[XMSG_CANT_INSRT_CHILD_INTO_PROP]" +" '" + prop_name + "' "+ "because property does not exist");
}
if (!p.isStrong) {
throw new Error(-1, "#TEXT[XMSG_CANT_INSRT_CHILD_INTO_PROP]"+" '" + prop_name + "' "+ "because it is not marked strong");
}
var t = this.Class.properties[prop_name].type.charAt(0);
var o = this[prop_name];
if (t == 'p')
this[prop_name] = child; //single object
else if (t == 'c') {
if (!this[prop_name]) this[prop_name] = {};
this[prop_name][child.id] = child; //collection
} else {
if (!this[prop_name]) this[prop_name] = [];
before = INT(before);
if(before <= this[prop_name].length){
this[prop_name].splice(before,0,child);
}else{
this[prop_name].push(child); //array
}
}
child.parent_prop = prop_name;
end
sealed method clearChild(child, prop_name)
var index = null ;
var p = this.Class.properties[prop_name];
if (!p) {
throw new Error(-1, "#TEXT[XMSG_CANNOT_CLEAR_CHILD_IN_PROP]" +" '" + prop_name + "' "+ "because property does not exist");
}
if (!p.isStrong) {
throw new Error(-1, "#TEXT[XMSG_CANNOT_CLEAR_CHILD_IN_PROP]" +" '" + prop_name + "' "+ "because it is not marked strong");
}
var t = this.Class.properties[prop_name].type.charAt(0);
if (t == 'p')
this[prop_name] = null; //single object
else if (t == 'c')
delete this.getProperty(prop_name)[child.id]; //collection
else {
//TODO: test !!! because currently we do not use Array anywhere
var arr = this.getProperty(prop_name); //delete from array and shift all remaining elements
for (var i = 0; i
Inserts a given element under this object
The child element to insert
The property name to insert the child element to. (Must be a property marked as 'strong')
Integer that specifies the placement of the new element.
If this parameter is specified, the child element will be inserted immediately before the specified index.
This parameter is only relevent to properties of type vector
Indicates whether the element was successfully inserted
This method is sealed and cannot be overridden on sub-classes. If needed, you can override the @onInsert and onInsertMe callback method to extend the insertion operation.
If the child element already belongs to another parent object, then it will be removed from the old parent as part of this operation.
This method always results in a single transaction, even if the inserted child is a composite element. The composite element and its sub-elements implicitly belong to the same transaction, and do not contribute separate transaction steps. Similarly, notification events are raised only on the composite element, and not on any of its sub-elements.
TIP: When creating a composite element, it is more efficient to create the element and its sub-elements off the model, before inserting the composite element to the model (see Model's @Model!createElement method).
sealed method insertElement(child, prop_name, before,subType)
try {
// check child validity for insertion
if (!child) return false;
if (!this.children) this.children={}; // initilize children property as empty collection if needed
if (child.parent === this) return true;
var msg1='#TEXT[XMSG_ERROR_IN_INSERT_ELEMENT]';
#DEVTIME[
if (prop_name == undefined || typeof prop_name != 'string')
throw new Error(-1, msg1);
]
var p = this.Class.properties[prop_name]; //should be in DEVTIME pragma, but [] problem
var msg2='#TEXT[XMSG_FAIL_TYPE_CHECK_INSRT_ELEM]';
var msg3='when expecting type';
#DEVTIME[
if (p && p.typecheck && !ISA(child, p.typecheck))
throw new Error(-1, msg2+" " + child.Class.fullname + " "+msg3+" " + p.typecheck);
]
// if this object is floating - do a non-transactional insertion
var unit = (this.isa('core.gml:Unit') ? this : this.unit) || null;
if (!unit && !child.parent) {
child.parent = this;
child.unit = null;
this.children[child.id] = child;
this.assignChild(child, prop_name ,before);
if (this.onInsert) this.onInsert(child);
if (child.onInsertMe) child.onInsertMe(this);
return true;
}
// send event to test that the element can be inserted
var evt = document.createEventObject();
evt.type = 'canInsertElement';
evt.object = this;
evt.child = child;
evt.unit = unit;
evt.prop_name = prop_name;
evt.cancel = false;
evt.subType = subType;
$ENV.fireModelEvent(evt);
if (evt.cancel) return false;
// if element has no old parent - do a transactional insertion
if (!child.parent && $ENV.recorder.execute('doInsertElement', unit, this, child, prop_name,before,subType) != null)
return true;
// TODO: HOW DO WE KNOW WHERE THE ELEMENT IS REMOVED FROM ???
// test that the element can be removed from old parent
var evt = document.createEventObject();
evt.type = 'canRemoveElement';
evt.object = child.parent;
evt.child = child;
evt.unit = child.unit;
evt.subType = subType;
evt.cancel = false;
$ENV.fireModelEvent(evt);
if (evt.cancel) return false;
try{
// do a transactional transfer from old to new parent
BEGIN('transfer element');
var index;
if ($ENV.recorder.execute('doRemoveElement', child.unit, child.parent, child, prop_name, index,subType) != null &&
$ENV.recorder.execute('doInsertElement', unit, this, child, prop_name,before,subType) != null)
COMMIT();
else
throw new Error(-1, "Error in insertElement: null returned");
}catch(e){
ROLLBACK();
var unitMsg = child.unit ? ' in unit ' + child.unit.id : '' ;
var childId = (child && child.id || '' );
var parentId = child.parent && child.parent.id || '' ;
#LOG[4, 'Error while trying to insert child element '+ childId +' to parent ' + parentId + unitMsg+ ", Desc: " + e.description];
return false;
}
return true;
} catch(e) {
#LOG[4, e.description];
return false;
}
end
<@doc>
Removes a child element from this object
The child element to remove
The property name to remove the child element from. (Must be a property marked as 'strong')
The Id of the child element to remove
The property name to remove the child element from. (Must be a property marked as 'strong')
Indicates whether the child element was successfully removed
The given child element must be contained in the ~prop_name property of this object
This method is sealed and cannot be overridden on sub-classes. If needed, you can override the @onRemove callback method to extend the removal operation.
This method always results in a single transaction, even if the removed child is a composite element. The composite element and its sub-elements implicitly belong to the same transaction, and do not contribute separate transaction steps. Similarly, notification events are raised only on the removed element, and not on any of its sub-elements.
sealed method removeElement(child, prop_name)
if (!this.children) return false;
if (typeof child == 'string') child = this.children[child];
if (!child) return false;
if (!child.parent) return true;
if (child.parent != this) return false;
var msg1='#TEXT[XMSG_ERROR_IN_REMOVE_ELEMENT]';
#DEVTIME[
if (prop_name == undefined || typeof prop_name != 'string')
throw new Error(-1, msg1);
]
var p = this.Class.properties[prop_name]; //should be in DEVTIME pragma, but [] problem
var msg2='#TEXT[XMSG_FAIL_TYPE_CHECK_REMOVEELEM]';
var msg3='when expecting type';
#DEVTIME[
if (p && p.typecheck && !ISA(child, p.typecheck))
throw new Error(-1, msg2+" " + child.Class.fullname + " "+ msg3 + " " + p.typecheck);
]
// if this object is floating - do a non-transactional removal
var unit = (this.isa('core.gml:Unit') ? this : this.unit) || null;
if (!unit) {
if (this.onRemove) this.onRemove(child);
if (child.onRemoveMe) child.onRemoveMe(this);
child.parent.clearChild(child, prop_name);
delete this.children[child.id];
child.id = null;
child.parent = null;
child.unit = null;
return true;
}
// test that the child element can be removed
var evt = document.createEventObject();
evt.type = 'canRemoveElement';
evt.object = this;
evt.child = child;
evt.unit = unit;
evt.cancel = false;
evt.prop_name = prop_name;
$ENV.fireModelEvent(evt);
if (evt.cancel) return false;
// do a transactional removal
try{
BEGIN('Remove element');
var index;
if ($ENV.recorder.execute('doRemoveElement', unit, this, child, prop_name,index) == null)
throw new Error(-1, "Error in removeElement: doRemoveElement failed");
// If a usage was removed than update the model index collection
if ( ISA(child, 'core.gml:Usage'))
$ENV.organizer.updateIndex();
COMMIT();
}catch(e){
ROLLBACK();
var unitMsg = unit ? ' in unit ' + unit.id : '' ;
var childId = (child && child.id || '' );
#LOG[4, 'Error while trying to remove child element '+ childId +' from parent ' + this.id +unitMsg+ ", Desc: " + e.description];
return false;
}
return true;
end
<@doc>
Check if the object or its children have translation implications - currently this means if it has links to Gml:Text objects
~true if it does have translation implications, ~false otherwise
method hasTranslation(deep)
return(false); //nothing for TransObject. The method is defined here because it is called by hasChildTranslation
end
<@doc>
Check if the object's children have translation implications - currently this means if it has links to Gml:Text objects
~true if it does have translation implications, ~false otherwise
method hasChildTranslation()
var val, isStrong, props = this.Class.properties;
for (var k in props) {
if (this.hasProperty(k)) {
val = this.getProperty(k);
isStrong = props[k].isStrong;
if ((val != undefined) && isStrong) {
var t = props[k].type.charAt(0);
switch (t) {
case 'p':
if (val.hasTranslation(true))
return(true);
break;
case 'c': // collection
for(var i in val)
if (val[i].hasTranslation(true))
return(true);
break;
case 'v': // vector
for(var i in val)
if(val[i].hasTranslation(true))
return(true);
break;
default:
return(false);
}
}
}
}
return(false); //nothing found
end
<@doc>
Check if the object has personalization implications
~true if it does has personalization implications, ~false otherwise
method canBePersonalized()
return(false); // false by default;
end
<@doc>
Tell if the object or it's expected children can have properties that can be translated.
~true if strings are possible, ~false otherwise
This is similar in usage to hasTranslation. The difference is that here the check is static, based on the Class.
This means that the result might be wrong, but the performance is very fast.
Use this method in cases the actual existance of strings is not visible to the modeler, and performance of hasTranslation might be an issue.
method canBeTranslated()
return(false); // false by default;
end
<@doc>
Handler method for cloning operation: default implementation copies all
properties and clones all children.
This method should not be called directly as it does not handle all aspects of the clone operation as fixing
references, initialization of the cloned objects and after clone ooperations needed to complete this action.
Instead, to clone an object you should either call Model's @gml:Model!cloneElements method or ObjectSet->ObjectSet!clone method.
When overriding this method, one should consider that only at the time of GmlObject!onAfterClone method, the object's properties
are set properly after fixing references and initializing the object.
This parameter is an [OUT] parameter and passed byref: Map of the newly created objects used to fix object references.
This map should be populated with newly created objects for any depth of children created indirectly by this clone operation.
The ID used for this map should be of the old object from which the new one was cloned
Parent object of the newly cloned object
The newly cloned object
method onClone(elementsMap, parent)
var onloadList=[];
if ( !elementsMap ) elementsMap = {};
var copy = $ENV.createRawObject(this.Class.fullname, cloneProperties(this));
var model = this.model || (this.unit? this.unit.model: $ENV.context.model);
copy.id = model.newId();
delete copy.unit;
elementsMap[this.id] = copy;
if (parent) {
copy.parent = parent;
if ( parent.children == null ) parent.children = {};
parent.children[copy.id] = copy;
}
// clone children
if (this.children)
{
for (var k in this.children)
{
var child = this.children[k].onClone(elementsMap, copy);
}
}
//TODO: rewrite so that we clone deep the strong properties, with prop_names ??
return copy;
function cloneProperties(elem) {
var P=elem.Class.properties, V={};
for (var name in P) {
var prop=P[name], value=elem[name];
if (!elem.hasOwnProperty(name) || name== 'id') continue;
if ((!prop.isVirtual || (prop.name == 'parent_prop')) && !prop.isStatic) V[name] = CLONE(value);
}
return V;
}
end
<@doc scope="private">
This method goes over all the object's propertis and translaets all its references to other objects,
using the provided elemens map. the elementsMap parameter is created during the onClone operation.
(This method is used in ObjectSet, for the clone mechanism)
A collection that maps the 'old object's ID to a new object
method onFixClonedReferences(elementsMap)
{
var P=this.Class.properties;
for (var name in P)
{
var prop=P[name], value=this[name];
if (!this.hasOwnProperty(name) || !value) continue;
switch (prop.type)
{
case 'pointer':
var ptr = elementsMap[value.id];
if (ptr) this[name]=ptr;
break;
case 'vector':
for (var i=0, len=value.length; i
A callback method that is invoked when a child object is inserted into this object
The child object that was inserted
The transaction context
Use the ~trans argument to determine the context under which this method was invoked.
This method can be invoked as a result of doing or redoing an insertion transaction or as
a result of undoing a removal transaction.
The method is invoked before any notification events are raised.
Override this method to create or update dependant model objects, as needed.
// abstract method
/*
method onInsert(child , trans)
if ( child.isa('core.gml:Usage')){
$ENV.organizer.addUsage(child, trans.unit);
}else if(child.isa('#NS[Interactor]') || child.isa('#NS[Port]') || child.isa('#NS[State]') ){
$ENV.organizer.addElement(child,trans.unit);
}
end
*/
<@method name="onInsertMe">
A callback method that is invoked when a this object is inserted into a paernt object
The parent object that this object was inserted to
The transaction context
This callback is always called after the @onInsert callback was called for the parent
Use the ~trans argument to determine the context under which this method was invoked.
This method can be invoked as a result of doing or redoing an insertion transaction or as a result of undoing a removal transaction.
The method is invoked before any notification events are raised.
Override this method to create or update dependant model objects, as needed.
// abstract method
<@method name="onRemove">
A callback method that is invoked when a child object is removed from this object
The child object that was removed
The transaction context
Use the ~trans argument to determine the context under which this method was invoked.
This method can be invoked as a result of doing or redoing a removal transaction or as
a result of undoing an insertion transaction.
The method is invoked before any notification events are raised.
Override this method to create or update dependant model objects, as needed.
// abstract method
/*
method onRemove(child , trans)
if ( child.isa('core.gml:Usage')){
$ENV.organizer.delUsage(child, trans.unit);
}else if(child.isa('#NS[Interactor]') || child.isa('#NS[Port]') || child.isa('#NS[State]') ){
$ENV.organizer.delElement(child,trans.unit);
}
end
*/
<@method name="onRemoveMe">
A callback method that is invoked when a this object is removed from its parent object
The parent object that this object was removed from
The transaction context
This callback is always called after the @onRemove callback was called for the parent.
Use the ~trans argument to determine the context under which this method was invoked.
This method can be invoked as a result of doing or redoing a removal transaction or as a result of undoing an insertion transaction.
The method is invoked before any notification events are raised.
Override this method to create or update dependant model objects, as needed.
// abstract method
<@method name="onPropagate">
A callback method that is invoked when an object is notified regarding the change of the model.
The event object which describes the model's change.
The specific object instance is notified when it potentially can be affected by the model's change.
Here is the list of the propagated events:
@gml:TransObject!onInsertElement | @Port!, @Connectable!, @Link!
@gml:TransObject!onRemoveElement | @Port!, @Connectable!, @Link!
// abstract method
///////////////////////////////////////////////////////////////////////
// EVENTS
<@doc group="III. Events">
Queries whether a child element can be inserted into a given object
The parent object
The child element to be inserted
The containing unit
The name of the property into which this element should be inserted
Set this flag to ~true to cancel the child element insertion
event canInsertElement(object, child, unit, prop_name, cancel);
<@doc>
Notifies that a child element has been inserted
The parent object
The child element that was inserted
The containing unit
The name of the property to which this element was inserted
event onInsertElement(object, child, prop_name, unit);
<@doc>
Queries whether a child element can be removed from its parent object
The parent object
The child element to be removed
The containing unit
The name of the property from which this element should be removed
Set this flag to ~true to cancel the child element removal
event canRemoveElement(object, child, unit, prop_name, cancel);
<@doc>
Notifies that a child element has been removed
The parent object
The child element that was removed
The containing unit
The name of the property from which this element was removed
event onRemoveElement(object, child, prop_name, unit);
<@doc>
Queries whether a given element can be cloned. This event may be raised more than once on the same element in a clone
transaction.
The object to be cloned
The collection of objects that are cloned in this transaction
Set this flag to ~true to cancel the cloning of this element only, allowing the rest of the clone operation to continue
Set this flag to ~true to cancel the entire cloning operation
event canCloneElement(object, objectset, cancelall, cancelme);
<@doc>
Queries whether a given foreign unit can be copied to a given target
GML classname of the source foreign unit
GML classname of the target
Set this flag to ~true to cancel the copy of the src
event canCopyForeignUnit (src, target, cancel);
<@doc>
Fires when an external editor is closed
The component which was edited using the editor
The id of the editor which was closed
The value of the returnValue property set by the editor's window
event onExternalEditorClose(object, editorId, returnValue);
///////////////////////////////////////////////////////////////////////
// TRANSACTIONS
transaction doSetProperty(object, name, value, oldval, transType)
do, redo:
var v0=value, v1=oldval;
undo:
var v0=oldval, v1=value;
all:
if (v0 === undefined)
{
delete object[name];
}
else
{
object[name] = v0;
}
object.translationHandling(name, value, v0, v1, transType);
// setDirty needsto know if the change was only on the SVG level - see model.gs
var isSVGChange=false;
if (object.Class.properties[name].Class.isAspect && "svg" == object.Class.properties[name].Class.prefix)
isSVGChange=true;
object.setDirty(isSVGChange);
var evt = document.createEventObject();
evt.type = 'onUpdateObject';
evt.object = object;
evt.name = name;
evt.value = v0;
evt.oldval = v1;
$ENV.fireModelEvent(evt);
if (name == 'name') {
var evt = document.createEventObject();
evt.type = 'onRenameObject';
evt.object = object;
evt.newname = v0;
evt.oldname = v1;
$ENV.fireModelEvent(evt);
}
end
//onlyCheck - execute (~false) or only test if translation will be removed (~true)
//return - ~true if trnalsation will be removed, ~false otherwise
<@doc scope="private">
handles translations for the doSetProperty transation. Also used to see if planned changed will cause translation loss.
The name of the property that is to be modified
The new value to assign
either old or new value, depends on transaction "direction" (do vs. undo)
either old or new value, depends on transaction "direction" (do vs. undo)
The type of translation (e.g. XBUTT)
~false does the setProperty action, ~true means we only check if the action leads to translation object loss
only used if onlyCheck=true. "enumeration" that tells what will happen to translation as a result of this operation
0=no change 1=translation modified 2=new translation 3=translation deleted
sealed method translationHandling(name, value, v0, v1, transType, onlyCheck)
// Support for translation:
if (!#SYS[SUPPORT_TRANSLATION]) // NO TRANSLATION SUPPORT...
return(#[TRANSLATE_NOP]);
var key = this.getTransKey(name);// check if the object already has a translation object (gml:Text)
try {
if (undefined===v0) //proeprty was already removed. Now handle translation
if (key) { //if translation does exist, we need to remove it, since proeprty was already removed
if (onlyCheck)
return(#[TRANSLATE_DEL]);
this.removeTranslation(key);
this.removeTransKey(name);
}
else // translation does not exist ==> it won't be deleted...
return(#[TRANSLATE_NOP]);
if(transType) { // if entered transaction with request to handle translation
value = TRIM(value);
if(!key && value && value != this.Class.properties[name].value) { // No current translation and value differs from default ==> create translation
// Insert the translation to the translation table:
if (onlyCheck) return(#[TRANSLATE_NEW]);
key = this.insertTranslation(transType, v0);
if(key) {
// Update the current object mapping:
this.setTransKey( name, key);
}
}
else { // either translation already exists, or it does not but value does not differ from default
if(value && value != this.Class.properties[name].value) { // if it's the case of new value with existing translation ==> only update translation with new text
// Translation already exists, update:
if (onlyCheck) return(#[TRANSLATE_MOD]);
this.removeTranslation(key);
this.removeTransKey(name);
key = this.insertTranslation(transType, v0);
//key = this.updateTranslation(v0, key); //NadavL - create new text object
}
else { // For empty property value or a value that equals the default:
if (key) { // translation exists but value equals default value ==> delete translation object
if (onlyCheck) return(#[TRANSLATE_DEL]);
this.removeTranslation(key);
this.removeTransKey(name);
}
if (onlyCheck) return(#[TRANSLATE_NOP]);
key = v0; // set field contents to new value
}
}
if (onlyCheck) return(#[TRANSLATE_NOP]);
this[name] = key;
}
else // we entered transaction with OUT a request to handle translation
if (key && (!value || value == this.Class.properties[name].value)) { // translation exists but is not required // empty property value or a value that equals the default:
if (onlyCheck) return(#[TRANSLATE_DEL]);
this.removeTranslation(key);
this.removeTransKey(name);
}
if (onlyCheck) return(#[TRANSLATE_NOP]);
}
catch(e) {
if (onlyCheck) return(#[TRANSLATE_NOP]); // NadavL - used to be return(#[TRANSLATE_DEL]);
// changed to due problems related to action addiotnal properites
if (key) {
// Error - remove existing redundent translation:
this.removeTranslation(key);
this.removeTransKey(name);
}
// Insertion failed:
PROMPT(e.description + "- See property '" + name + "'");
}
end
//onlyCheck - execute (~false) or only test if translation will be removed (~true)
//return - ~true if trnalsation will be removed, ~false otherwise
<@doc>
calls translationHandling is "test only" mode
The name of the property that is to be modified
The new value to assign
either old or new value, depends on transaction "direction" (do vs. undo)
either old or new value, depends on transaction "direction" (do vs. undo)
The type of translation (e.g. XBUTT)
only used if onlyCheck=true. ~true if the action will lead to loss of translation obejcts, ~false otherwise
sealed method willTranslationBeDeleted(name, value, v0, v1, transType)
if (v0==v1) return(0); // if texts are identical ==> SET_INPUT would not have called setProperty ==> act the same
return(this.translationHandling(name, value, v0, v1, transType, true));
end
<@doc scope="private">
This method goes over all the object's propertis and translates all its references to other objects,
using the provided elemens map. the elementsMap parameter is is giving by the called mathod.
A collection that maps the 'old object's ID to a new object
A collection that maps the 'old object's ID to a new object
A collection for all the elements that has been visited, To prevent
circular reference .
This is generic function that should be used by the core exculsively, and will be rewritten
in the next release.
In the next release the core will only support cross unit refernece in:
1. Plugs - Port.
2. Usage - Unit.
method onFixCrossUnitRef(elementsMap, scope , visited)
// Stop Condition: check the scope of the fix, to operate only on the specified scope.
if( scope && !inScope(scope ,this ) ) return ;
if(!visited) visited = {};
// Stop condition: if the current element has been visited than return.
if(visited[this.id]) return;
// Update the visited collection.
visited[this.id] = true;
var P=this.Class.properties;
// Iterate on all the properties of the element.
for (var name in P)
{
var prop=P[name], value=this[name];
if (!this.hasOwnProperty(name) || !value) continue;
switch ( PREF(prop.type,1) )
{
case 'P': // pointer
var ptr = elementsMap[$ENV.model.getElementId(value)];
if (ptr) {
this[name]=ptr;
if(scope && ptr.onFixCrossUnitRef)
ptr.onFixCrossUnitRef(elementsMap ,scope ,visited);
}else{
if(scope && value.onFixCrossUnitRef)
value.onFixCrossUnitRef( elementsMap,scope , visited);
}
break;
case 'V': // vector
for (var i=0, len=value.length; i