webgme
Version:
Web-based Generic Modeling Environment
1,139 lines (959 loc) • 100 kB
JavaScript
/*globals define, _, WebGMEGlobal*/
/*jshint browser: true */
/**
* @author rkereskenyi / https://github.com/rkereskenyi
*/
define(['js/logger',
'js/util',
'js/Constants',
'js/Utils/GMEConcepts',
'js/Utils/ComponentSettings',
'js/NodePropertyNames',
'js/RegistryKeys',
'js/Widgets/DiagramDesigner/DiagramDesignerWidget.Constants',
'./MetaEditorControl.DiagramDesignerWidgetEventHandlers',
'./MetaRelations',
'./MetaEditorConstants',
'./MetaDocItem',
'js/Utils/PreferencesHelper',
'js/Controls/AlignMenu',
'js/Dialogs/Confirm/ConfirmDialog'
], function (Logger,
util,
CONSTANTS,
GMEConcepts,
ComponentSettings,
nodePropertyNames,
REGISTRY_KEYS,
DiagramDesignerWidgetConstants,
MetaEditorControlDiagramDesignerWidgetEventHandlers,
MetaRelations,
MetaEditorConstants,
MetaDocItem,
PreferencesHelper,
AlignMenu,
ConfirmDialog) {
'use strict';
var META_DECORATOR = 'MetaDecorator',
DOCUMENT_DECORATOR = 'DocumentDecorator',
WIDGET_NAME = 'DiagramDesigner',
BASIC_META_RULES_CONTAINER_NODE_ID = MetaEditorConstants.META_ASPECT_CONTAINER_ID,
NO_LIBRARY_SELECTED_TEXT = ' . ',
YOUTUBE_VIDEO_URL = 'https://www.youtube.com/playlist?list=PLhvSjgKmeyjhceY4ScQ1sLgiZG-x9WNHX';
function getMultiplicityText(min, max) {
var start = min === -1 ? '0' : '' + min,
end = max === -1 ? '*' : '' + max;
return start + '..' + end;
}
function isOneToOnePointer(ptrDesc) {
return ptrDesc.min === 1 && ptrDesc.max === 1;
}
function MetaEditorControl(options) {
var self = this;
this.logger = options.logger || Logger.create(options.loggerName || 'gme:Panels:MetaEditor:MetaEditorControl',
WebGMEGlobal.gmeConfig.client.log);
this._client = options.client;
this._config = MetaEditorControl.getDefaultConfig();
ComponentSettings.resolveWithWebGMEGlobal(this._config, MetaEditorControl.getComponentId());
//initialize core collections and variables
this.diagramDesigner = options.widget;
this._alignMenu = new AlignMenu(this.diagramDesigner.CONSTANTS, {});
if (this._client === undefined) {
this.logger.error('MetaEditorControl\'s client is not specified...');
throw ('MetaEditorControl can not be created');
}
if (this.diagramDesigner === undefined) {
this.logger.error('MetaEditorControl\'s DiagramDesigner is not specified...');
throw ('MetaEditorControl can not be created');
}
//in METAEDITOR mode DRAG & COPY is not enabled
this.diagramDesigner.enableDragCopy(false);
this._metaAspectMemberPatterns = {};
this._filteredOutConnTypes = [];
this._filteredOutConnectionDescriptors = {};
//local variable holding info about the currently opened node
this.currentNodeInfo = {id: null, members: []};
this._metaAspectMembersAll = [];
this._metaAspectMembersPerSheet = {};
this._metaAspectMembersCoordinatesPerSheet = {};
this._selectedMetaAspectSheetMembers = [];
this._selectedSheetID = null;
this._selectedLibrary = null;
this._metaDocItemsPerSheet = {};
this._ComponentID2DocItemID = {};
this._DocItemID2ComponentID = {};
this._GMEID2ComponentID = {};
this._ComponentID2GMEID = {};
//set default connection type to containment
this._setNewConnectionType(MetaRelations.META_RELATIONS.CONTAINMENT);
this._initFilterPanel();
//attach all the event handlers for event's coming from DiagramDesigner
this.attachDiagramDesignerWidgetEventHandlers();
//let the decorator-manager download the required decorator
this._client.decoratorManager.download([META_DECORATOR], WIDGET_NAME, function () {
self.logger.debug('MetaEditorControl ctor finished');
//load meta container node
//give the UI time to render first before start using it's features
setTimeout(function () {
self._loadMetaAspectContainerNode();
}, 10);
});
}
MetaEditorControl.prototype._getRootIdOfLibrary = function (nodeId) {
var node = this._client.getNode(nodeId),
rootId = BASIC_META_RULES_CONTAINER_NODE_ID;
if (node && (node.isLibraryElement() || node.isLibraryRoot())) {
while (!node.isLibraryRoot()) {
node = node.getNode(node.getParentId());
}
rootId = node.getId();
}
return rootId;
};
MetaEditorControl.prototype._setLibraryDropdownText = function () {
var node = this._client.getNode(this.metaAspectContainerNodeID);
if (node === null || this.metaAspectContainerNodeID === BASIC_META_RULES_CONTAINER_NODE_ID ||
this.metaAspectContainerNodeID === null || this.metaAspectContainerNodeID === undefined) {
this._toolbarLibraryList.dropDownText(NO_LIBRARY_SELECTED_TEXT);
this._selectedLibrary = '';
} else {
this._toolbarLibraryList.dropDownText(
node.getFullyQualifiedName()
);
this._selectedLibrary = node.getFullyQualifiedName();
}
};
MetaEditorControl.prototype._loadMetaAspectContainerNode = function (libraryName) {
var self = this;
this.currentNodeInfo.id = WebGMEGlobal.State.getActiveObject();
if (libraryName) {
this.metaAspectContainerNodeID =
this._client.getNode(BASIC_META_RULES_CONTAINER_NODE_ID).getLibraryRootId(libraryName);
} else if (libraryName === '') {
this.metaAspectContainerNodeID = BASIC_META_RULES_CONTAINER_NODE_ID;
} else {
this.metaAspectContainerNodeID = self._getRootIdOfLibrary(this.currentNodeInfo.id);
}
this.logger.debug('_loadMetaAspectContainerNode: "' + this.metaAspectContainerNodeID + '"');
this._initializeSelectedSheet();
this._refreshLibraryList();
if (typeof this.currentNodeInfo.id === 'string') {
if (this.metaAspectContainerNodeID === BASIC_META_RULES_CONTAINER_NODE_ID) {
this.setReadOnly(false);
this.diagramDesigner.setReadOnly(false);
} else {
this.setReadOnly(true);
this.diagramDesigner.setReadOnly(true);
}
}
//remove current territory patterns
if (this._territoryId) {
this._client.removeUI(this._territoryId);
}
//put new node's info into territory rules
this._selfPatterns = {};
this._selfPatterns[BASIC_META_RULES_CONTAINER_NODE_ID] = {children: 0};
this._selfPatterns[this.metaAspectContainerNodeID] = {children: 0};
//create and set territory
this._territoryId = this._client.addUI(this, function (events) {
self._eventCallback(events);
});
this._client.updateTerritory(this._territoryId, this._selfPatterns);
};
/**********************************************************/
/* PUBLIC METHODS */
/**********************************************************/
MetaEditorControl.prototype._eventCallback = function (events) {
var i = events ? events.length : 0,
e;
this.logger.debug('_eventCallback "' + i + '" items');
this.diagramDesigner.beginUpdate();
while (i--) {
e = events[i];
if (e.eid === BASIC_META_RULES_CONTAINER_NODE_ID && e.etype === CONSTANTS.TERRITORY_EVENT_UPDATE) {
this._refreshLibraryList();
}
if (e.eid === BASIC_META_RULES_CONTAINER_NODE_ID &&
this.metaAspectContainerNodeID !== BASIC_META_RULES_CONTAINER_NODE_ID &&
e.etype !== CONSTANTS.TERRITORY_EVENT_UNLOAD) {
continue;
}
switch (e.etype) {
case CONSTANTS.TERRITORY_EVENT_LOAD:
this._onLoad(e.eid);
break;
case CONSTANTS.TERRITORY_EVENT_UPDATE:
this._onUpdate(e.eid);
break;
case CONSTANTS.TERRITORY_EVENT_UNLOAD:
this._onUnload(e.eid);
break;
default:
break;
}
}
this.diagramDesigner.endUpdate();
this.diagramDesigner.hideProgressbar();
this.logger.debug('_eventCallback "' + events.length + '" items - DONE');
};
//might not be the best approach
MetaEditorControl.prototype.destroy = function () {
this._detachClientEventListeners();
this._removeToolbarItems();
this._selectedLibrary = null;
this._client.removeUI(this._territoryId);
this._client.removeUI(this._metaAspectMembersTerritoryId);
};
/**********************************************************/
/* LOAD / UPDATE / UNLOAD HANDLER */
/**********************************************************/
MetaEditorControl.prototype._onLoad = function (gmeID) {
if (gmeID === this.metaAspectContainerNodeID) {
this._processMetaAspectContainerNode();
} else {
this._processNodeLoad(gmeID);
}
};
MetaEditorControl.prototype._onUpdate = function (gmeID) {
if (gmeID === this.metaAspectContainerNodeID) {
this._processMetaAspectContainerNode();
} else {
this._processNodeUpdate(gmeID);
}
};
MetaEditorControl.prototype._onUnload = function (gmeID) {
var self = this;
if (gmeID === this.metaAspectContainerNodeID) {
//the opened model has been deleted....
//most probably a project / branch / whatever change
this.logger.debug('The currently opened aspect has been deleted --- GMEID: "' +
this.metaAspectContainerNodeID + '"');
setTimeout(function () {
self._loadMetaAspectContainerNode();
}, 10);
} else {
this._processNodeUnload(gmeID);
}
};
/**********************************************************/
/* END OF --- LOAD / UPDATE / UNLOAD HANDLER */
/**********************************************************/
/**********************************************************/
/* CUSTOM BUTTON EVENT HANDLERS */
/**********************************************************/
MetaEditorControl.prototype._printNodeData = function () {
//TODO could be filled with meaningful info
};
/**********************************************************/
/* END OF --- CUSTOM BUTTON EVENT HANDLERS */
/**********************************************************/
/***********************************************************/
/* PROCESS CURRENT NODE TO HANDLE ADDED / REMOVED ELEMENT */
/***********************************************************/
MetaEditorControl.prototype._processMetaAspectContainerNode = function () {
var aspectNodeID = this.metaAspectContainerNodeID,
aspectNode = this._client.getNode(aspectNodeID),
territoryChanged = false,
metaInconsistencies,
i,
len,
diff,
objDesc,
componentID,
gmeID,
selectedSheetMembers,
positionsUpdated;
//this._metaAspectMembersAll contains all the currently known members of the meta aspect
this._metaAspectMembersAll = aspectNode.getMemberIds(MetaEditorConstants.META_ASPECT_SET_NAME);
//setSelected sheet
//this._selectedMetaAspectSet
//process the sheets
positionsUpdated = this._processMetaAspectSheetsRegistry();
//check to see if the territory needs to be changed
//the territory contains the nodes that are on the currently opened sheet
//this._selectedMetaAspectSheetMembers
selectedSheetMembers = this._metaAspectMembersPerSheet[this._selectedMetaAspectSet] || [];
//check deleted nodes
diff = _.difference(this._selectedMetaAspectSheetMembers, selectedSheetMembers);
len = diff.length;
while (len--) {
delete this._metaAspectMemberPatterns[diff[len]];
territoryChanged = true;
}
//check added nodes
diff = _.difference(selectedSheetMembers, this._selectedMetaAspectSheetMembers);
len = diff.length;
while (len--) {
this._metaAspectMemberPatterns[diff[len]] = {children: 0};
territoryChanged = true;
}
//check all other nodes for position change
//or any other change that could have happened (local registry modifications)
//diff = positionsUpdated;//_.intersection(this._selectedMetaAspectSheetMembers, selectedSheetMembers);
diff = _.intersection(this._selectedMetaAspectSheetMembers, selectedSheetMembers);
len = diff.length;
while (len--) {
gmeID = diff[len];
objDesc = {position: {x: 100, y: 100}};
if (this._metaAspectMembersCoordinatesPerSheet[this._selectedMetaAspectSet][gmeID]) {
objDesc.position.x = this._metaAspectMembersCoordinatesPerSheet[this._selectedMetaAspectSet][gmeID].x;
objDesc.position.y = this._metaAspectMembersCoordinatesPerSheet[this._selectedMetaAspectSet][gmeID].y;
}
if (this._GMEID2ComponentID.hasOwnProperty(gmeID)) {
componentID = this._GMEID2ComponentID[gmeID];
this.diagramDesigner.updateDesignerItem(componentID, objDesc);
}
}
this._selectedMetaAspectSheetMembers = selectedSheetMembers.slice(0);
this._processMetaDocItems();
metaInconsistencies = this._config.autoCheckMetaConsistency ? this._client.checkMetaConsistency() : [];
for (i = 0; i < metaInconsistencies.length; i += 1) {
this._client.notifyUser(metaInconsistencies[i]);
}
if (metaInconsistencies.length > 0) {
this._client.notifyUser({
severity: 'error',
message: 'Click the check-mark furthest to the right in the MetaEditor\'s header toolbar for more details.'
});
}
//there was change in the territory
if (territoryChanged === true) {
this._client.updateTerritory(this._metaAspectMembersTerritoryId, this._metaAspectMemberPatterns);
}
};
/**********************************************************************/
/* END OF --- PROCESS CURRENT NODE TO HANDLE ADDED / REMOVED ELEMENT */
/**********************************************************************/
MetaEditorControl.prototype._processMetaDocItems = function () {
var docItemsInRegistry,
docItemId,
decClass,
uiComponent,
componentId,
objDesc;
this.diagramDesigner.beginUpdate();
if (this._selectedMetaAspectSet && this._metaDocItemsPerSheet[this._selectedMetaAspectSet]) {
docItemsInRegistry = this._metaDocItemsPerSheet[this._selectedMetaAspectSet];
} else {
docItemsInRegistry = {};
}
for (docItemId in this._DocItemID2ComponentID) {
if (docItemsInRegistry.hasOwnProperty(docItemId) === false) {
// "unload"
this.diagramDesigner.deleteComponent(this._DocItemID2ComponentID[docItemId]);
componentId = this._DocItemID2ComponentID[docItemId];
delete this._ComponentID2DocItemID[componentId];
delete this._DocItemID2ComponentID[docItemId];
}
}
for (docItemId in docItemsInRegistry) {
if (this._DocItemID2ComponentID.hasOwnProperty(docItemId)) {
// "update"
objDesc = {};
objDesc.position = docItemsInRegistry[docItemId].position;
componentId = this._DocItemID2ComponentID[docItemId];
this.diagramDesigner.updateDesignerItem(componentId, objDesc);
} else {
// "load"
decClass = this._client.decoratorManager.getDecoratorForWidget(DOCUMENT_DECORATOR, WIDGET_NAME);
objDesc = docItemsInRegistry[docItemId].getObjectDescriptor(decClass);
uiComponent = this.diagramDesigner.createDesignerItem(objDesc);
this._DocItemID2ComponentID[docItemId] = uiComponent.id;
this._ComponentID2DocItemID[uiComponent.id] = docItemId;
}
}
this.diagramDesigner.endUpdate();
};
/**************************************************************************/
/* HANDLE OBJECT LOAD --- DISPLAY IT WITH ALL THE POINTERS / SETS / ETC */
/**************************************************************************/
MetaEditorControl.prototype._processNodeLoad = function (gmeID) {
var nodeObj = this._client.getNode(gmeID),
ownJsonMeta = nodeObj.getOwnJsonMeta(),
uiComponent,
decClass,
objDesc;
//component loaded
if (this._GMENodes.indexOf(gmeID) === -1) {
//aspect's member has been loaded
objDesc = {position: {x: 100, y: 100}};
if (this._metaAspectMembersCoordinatesPerSheet[this._selectedMetaAspectSet][gmeID]) {
objDesc.position.x = this._metaAspectMembersCoordinatesPerSheet[this._selectedMetaAspectSet][gmeID].x;
objDesc.position.y = this._metaAspectMembersCoordinatesPerSheet[this._selectedMetaAspectSet][gmeID].y;
}
decClass = this._client.decoratorManager.getDecoratorForWidget(META_DECORATOR, WIDGET_NAME);
objDesc.decoratorClass = decClass;
objDesc.control = this;
objDesc.metaInfo = { selectedLibrary: this._selectedLibrary };
objDesc.metaInfo[CONSTANTS.GME_ID] = gmeID;
//each meta specific registry customization will be stored in the MetaContainer node's main META SET
// (MetaEditorConstants.META_ASPECT_SET_NAME)
objDesc.preferencesHelper = PreferencesHelper.getPreferences([{
containerID: this.metaAspectContainerNodeID,
setID: MetaEditorConstants.META_ASPECT_SET_NAME
}]);
uiComponent = this.diagramDesigner.createDesignerItem(objDesc);
this._GMENodes.push(gmeID);
this._GMEID2ComponentID[gmeID] = uiComponent.id;
this._ComponentID2GMEID[uiComponent.id] = gmeID;
// Add relation connections
this._processNodeMetaContainment(gmeID, ownJsonMeta.children || {items: []});
this._processNodeMetaPointers(gmeID, ownJsonMeta.pointers || {});
this._processNodeMetaInheritance(gmeID, nodeObj.getBaseId());
this._processNodeMixins(gmeID, ownJsonMeta.mixins || []);
//check all the waiting pointers (whose SRC/DST is already displayed and waiting for the DST/SRC to show up)
//it might be this new node
this._processConnectionWaitingList(gmeID);
}
};
MetaEditorControl.prototype._processConnectionWaitingList = function (gmeID) {
var len,
gmeSrcID,
gmeDstID,
connType,
connTexts,
c = [];
//check for possible endpoint as gmeID
gmeDstID = gmeID;
if (this._connectionWaitingListByDstGMEID && this._connectionWaitingListByDstGMEID.hasOwnProperty(gmeDstID)) {
for (gmeSrcID in this._connectionWaitingListByDstGMEID[gmeDstID]) {
if (this._connectionWaitingListByDstGMEID[gmeDstID].hasOwnProperty(gmeSrcID)) {
len = this._connectionWaitingListByDstGMEID[gmeDstID][gmeSrcID].length;
while (len--) {
connType = this._connectionWaitingListByDstGMEID[gmeDstID][gmeSrcID][len][0];
connTexts = this._connectionWaitingListByDstGMEID[gmeDstID][gmeSrcID][len][1];
c.push({
gmeSrcID: gmeSrcID,
gmeDstID: gmeDstID,
connType: connType,
connTexts: connTexts
});
}
}
}
delete this._connectionWaitingListByDstGMEID[gmeDstID];
}
//check for possible source as gmeID
gmeSrcID = gmeID;
if (this._connectionWaitingListBySrcGMEID && this._connectionWaitingListBySrcGMEID.hasOwnProperty(gmeSrcID)) {
for (gmeDstID in this._connectionWaitingListBySrcGMEID[gmeSrcID]) {
if (this._connectionWaitingListBySrcGMEID[gmeSrcID].hasOwnProperty(gmeDstID)) {
len = this._connectionWaitingListBySrcGMEID[gmeSrcID][gmeDstID].length;
while (len--) {
connType = this._connectionWaitingListBySrcGMEID[gmeSrcID][gmeDstID][len][0];
connTexts = this._connectionWaitingListBySrcGMEID[gmeSrcID][gmeDstID][len][1];
c.push({
gmeSrcID: gmeSrcID,
gmeDstID: gmeDstID,
connType: connType,
connTexts: connTexts
});
}
}
}
delete this._connectionWaitingListBySrcGMEID[gmeSrcID];
}
len = c.length;
while (len--) {
gmeSrcID = c[len].gmeSrcID;
gmeDstID = c[len].gmeDstID;
connType = c[len].connType;
connTexts = c[len].connTexts;
this._createConnection(gmeSrcID, gmeDstID, connType, connTexts);
}
};
/**************************************************************************/
/* END OF --- HANDLE OBJECT LOAD DISPLAY IT WITH ALL THE POINTERS / ... */
/**************************************************************************/
/****************************************************************************/
/* HANDLE OBJECT UNLOAD --- DISPLAY IT WITH ALL THE POINTERS / SETS / ETC */
/****************************************************************************/
MetaEditorControl.prototype._processNodeUnload = function (gmeID) {
var self = this,
componentID,
idx,
len,
i,
otherEnd,
aConns,
connectionID;
if (this._GMEID2ComponentID.hasOwnProperty(gmeID)) {
componentID = this._GMEID2ComponentID[gmeID];
//gather all the information that is stored in this node's META
//CONTAINMENT
this._nodeMetaContainment[gmeID].items
.forEach(function (target) {
self._removeConnection(gmeID, target, MetaRelations.META_RELATIONS.CONTAINMENT);
});
// POINTERS/SETS
Object.keys(this._nodeMetaPointers[gmeID])
.forEach(function (ptrName) {
var ptrDesc = self._nodeMetaPointers[gmeID][ptrName],
connType = isOneToOnePointer(ptrDesc) ?
MetaRelations.META_RELATIONS.POINTER : MetaRelations.META_RELATIONS.SET;
ptrDesc.items
.forEach(function (target) {
self._removeConnection(gmeID, target, connType, ptrName);
});
});
//INHERITANCE
if (typeof this._nodeMetaInheritance[gmeID] === 'string') {
this._removeConnection(gmeID,
this._nodeMetaInheritance[gmeID],
MetaRelations.META_RELATIONS.INHERITANCE);
}
//MIXINS
for (i = 0; i < this._nodeMixins[gmeID].length; i += 1) {
this._removeConnection(gmeID, this._nodeMixins[gmeID][i], MetaRelations.META_RELATIONS.MIXIN);
}
//finally delete the guy from the screen
this.diagramDesigner.deleteComponent(componentID);
delete this._ComponentID2GMEID[componentID];
delete this._GMEID2ComponentID[gmeID];
idx = this._GMENodes.indexOf(gmeID);
this._GMENodes.splice(idx, 1);
//check if there is any more connection present that's associated with this object
//typically the connection end is this guy
//if so, remove but save to savedList
aConns = this._getAssociatedConnections(gmeID);
len = aConns.src.length;
while (len--) {
connectionID = aConns.src[len];
if (this._connectionListByID[connectionID]) {
//save the connection to the waiting list, since the destination is still there
// this._saveConnectionToWaitingList(this._connectionListByID[connectionID].GMESrcId,
// this._connectionListByID[connectionID].GMEDstId,
// this._connectionListByID[connectionID].type,
// this._connectionListByID[connectionID].connTexts);
this._removeConnection(this._connectionListByID[connectionID].GMESrcId,
this._connectionListByID[connectionID].GMEDstId,
this._connectionListByID[connectionID].type);
}
}
len = aConns.dst.length;
while (len--) {
connectionID = aConns.dst[len];
if (this._connectionListByID[connectionID]) {
//save the connection to the waiting list, since the destination is still there
// this._saveConnectionToWaitingList(this._connectionListByID[connectionID].GMESrcId,
// this._connectionListByID[connectionID].GMEDstId,
// this._connectionListByID[connectionID].type,
// this._connectionListByID[connectionID].connTexts);
this._removeConnection(this._connectionListByID[connectionID].GMESrcId,
this._connectionListByID[connectionID].GMEDstId,
this._connectionListByID[connectionID].type);
}
}
//check the waiting list and remove any connection that was waiting and this end was present
for (otherEnd in this._connectionWaitingListBySrcGMEID) {
if (this._connectionWaitingListBySrcGMEID.hasOwnProperty(otherEnd)) {
delete this._connectionWaitingListBySrcGMEID[otherEnd][gmeID];
if (_.isEmpty(this._connectionWaitingListBySrcGMEID[otherEnd])) {
delete this._connectionWaitingListBySrcGMEID[otherEnd];
}
}
}
for (otherEnd in this._connectionWaitingListByDstGMEID) {
if (this._connectionWaitingListByDstGMEID.hasOwnProperty(otherEnd)) {
delete this._connectionWaitingListByDstGMEID[otherEnd][gmeID];
if (_.isEmpty(this._connectionWaitingListByDstGMEID[otherEnd])) {
delete this._connectionWaitingListByDstGMEID[otherEnd];
}
}
}
}
//keep up accounting
delete this._nodeMetaContainment[gmeID];
delete this._nodeMetaPointers[gmeID];
delete this._nodeMetaInheritance[gmeID];
delete this._nodeMixins[gmeID];
};
/****************************************************************************/
/* END OF --- HANDLE OBJECT UNLOAD */
/****************************************************************************/
/****************************************************************************/
/* CREATE A SPECIFIC TYPE OF CONNECTION BETWEEN 2 GME OBJECTS */
/****************************************************************************/
MetaEditorControl.prototype._createConnection = function (gmeSrcId, gmeDstId, connType, connTexts) {
var connDesc,
connComponent,
metaInfo;
//need to check if the src and dst objects are displayed or not
//if YES, create connection
//if NO, store information in a waiting queue
if (this._GMENodes.indexOf(gmeSrcId) !== -1 && this._GMENodes.indexOf(gmeDstId) !== -1) {
//source and destination is displayed
if (this._filteredOutConnTypes.indexOf(connType) === -1) {
//connection type is not filtered out
connDesc = {
srcObjId: this._GMEID2ComponentID[gmeSrcId],
srcSubCompId: undefined,
dstObjId: this._GMEID2ComponentID[gmeDstId],
dstSubCompId: undefined,
reconnectable: false,
name: '',
nameEdit: false
};
//set visual properties
_.extend(connDesc, MetaRelations.getLineVisualDescriptor(connType));
//fill out texts
if (connTexts) {
_.extend(connDesc, connTexts);
}
connComponent = this.diagramDesigner.createConnection(connDesc);
//set connection metaInfo and store connection type
//the MetaDecorator uses this information when queried for connectionArea
metaInfo = {};
metaInfo[MetaRelations.CONNECTION_META_INFO.TYPE] = connType;
connComponent.setMetaInfo(metaInfo);
this._saveConnection(gmeSrcId, gmeDstId, connType, connComponent.id, connTexts);
} else {
//connection type is filtered out
this._filteredOutConnectionDescriptors[connType].push([gmeSrcId, gmeDstId, connTexts]);
}
} else {
//source or destination is not displayed, store it in a queue
this._saveConnectionToWaitingList(gmeSrcId, gmeDstId, connType, connTexts);
}
};
MetaEditorControl.prototype._saveConnectionToWaitingList = function (gmeSrcId, gmeDstId, connType, connTexts) {
if (this._GMENodes.indexOf(gmeSrcId) !== -1 && this._GMENodes.indexOf(gmeDstId) === -1) {
//#1 - the destination object is missing from the screen
this._connectionWaitingListByDstGMEID[gmeDstId] = this._connectionWaitingListByDstGMEID[gmeDstId] || {};
this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId] =
this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId] || [];
this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId].push([connType, connTexts]);
} else if (this._GMENodes.indexOf(gmeSrcId) === -1 && this._GMENodes.indexOf(gmeDstId) !== -1) {
//#2 - the source object is missing from the screen
this._connectionWaitingListBySrcGMEID[gmeSrcId] = this._connectionWaitingListBySrcGMEID[gmeSrcId] || {};
this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId] =
this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId] || [];
this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId].push([connType, connTexts]);
} else {
//#3 - both gmeSrcId and gmeDstId is missing from the screen
//NOTE: this should never happen!!!
this.logger.error('_saveConnectionToWaitingList both gmeSrcId and gmeDstId is undefined...');
}
};
MetaEditorControl.prototype._saveConnection = function (gmeSrcId, gmeDstId, connType, connComponentId, connTexts) {
//save by SRC
this._connectionListBySrcGMEID[gmeSrcId] = this._connectionListBySrcGMEID[gmeSrcId] || {};
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId] = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId] || {};
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType] =
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType] || [];
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].push(connComponentId);
//save by DST
this._connectionListByDstGMEID[gmeDstId] = this._connectionListByDstGMEID[gmeDstId] || {};
this._connectionListByDstGMEID[gmeDstId][gmeSrcId] = this._connectionListByDstGMEID[gmeDstId][gmeSrcId] || {};
this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType] =
this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType] || [];
this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType].push(connComponentId);
//save by type
this._connectionListByType[connType] = this._connectionListByType[connType] || [];
this._connectionListByType[connType].push(connComponentId);
//save by connectionID
this._connectionListByID[connComponentId] = {
GMESrcId: gmeSrcId,
GMEDstId: gmeDstId,
type: connType,
name: (connTexts && connTexts.name) ? connTexts.name : undefined,
connTexts: connTexts
};
};
MetaEditorControl.prototype._isPointerOrSetAndConnDescDoesNotMatchName = function (connDesc, connType, pointerOrSetName) {
return (connType === MetaRelations.META_RELATIONS.POINTER || connType === MetaRelations.META_RELATIONS.SET) &&
pointerOrSetName &&
pointerOrSetName !== '' &&
connDesc.name !== pointerOrSetName;
};
/****************************************************************************/
/* END OF --- CREATE A SPECIFIC TYPE OF CONNECTION BETWEEN 2 GME OBJECTS */
/****************************************************************************/
/****************************************************************************/
/* REMOVES A SPECIFIC TYPE OF CONNECTION FROM 2 GME OBJECTS */
/****************************************************************************/
MetaEditorControl.prototype._removeConnection = function (gmeSrcId, gmeDstId, connType, pointerOrSetName) {
var connectionID,
idx,
len,
connectionPresent = false;
//only bother if
//- both the source and destination is present on the screen
//the connection in question is drawn
if (this._connectionListBySrcGMEID[gmeSrcId] &&
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId] &&
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType]) {
connectionPresent = true;
}
if (!connectionPresent) {
return;
}
len = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].length;
while (len--) {
connectionID = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType][len];
// If a pointer or set with a specific name should be removed
// clear out the connectionID if this connection is not the representation of that pointer.
if (this._isPointerOrSetAndConnDescDoesNotMatchName(
this._connectionListByID[connectionID],
connType,
pointerOrSetName) === true) {
connectionID = undefined;
}
//if the connectionID is still valid
if (connectionID) {
this.diagramDesigner.deleteComponent(connectionID);
//clean up accounting
delete this._connectionListByID[connectionID];
idx = this._connectionListByType[connType].indexOf(connectionID);
this._connectionListByType[connType].splice(idx, 1);
idx = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].indexOf(connectionID);
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].splice(idx, 1);
idx = this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType].indexOf(connectionID);
this._connectionListByDstGMEID[gmeDstId][gmeSrcId][connType].splice(idx, 1);
}
}
};
/****************************************************************************/
/* END OF --- REMOVES A SPECIFIC TYPE OF CONNECTION FROM 2 GME OBJECTS */
/****************************************************************************/
/*****************************************************************************/
/* UPDATE CONNECTION TEXT */
/*****************************************************************************/
MetaEditorControl.prototype._updateConnectionText = function (gmeSrcId, gmeDstId, connType, connTexts) {
var connectionID,
idx,
len,
pointerOrSetName = connTexts.name,
found = false,
connectionPresent = false,
connDesc;
if (this._connectionListBySrcGMEID[gmeSrcId] &&
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId] &&
this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType]) {
connectionPresent = true;
}
if (!connectionPresent) {
return;
}
len = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType].length;
while (len--) {
connectionID = this._connectionListBySrcGMEID[gmeSrcId][gmeDstId][connType][len];
// If a pointer or set with a specific name should be updated
// clear out the connectionID if this connection is not the representation of that pointer.
if (this._isPointerOrSetAndConnDescDoesNotMatchName(
this._connectionListByID[connectionID],
connType,
pointerOrSetName) === true) {
connectionID = undefined;
}
//if the connectionID is still valid
if (connectionID) {
this._connectionListByID[connectionID].name = connTexts.name;
this._connectionListByID[connectionID].connTexts = connTexts;
this.diagramDesigner.updateConnectionTexts(connectionID, connTexts);
found = true;
}
}
if (!found) {
//try to find it in the connection waiting list
if (this._GMENodes.indexOf(gmeSrcId) !== -1 && this._GMENodes.indexOf(gmeDstId) === -1) {
//#1 - the destination object is missing from the screen
len = this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId].length;
for (idx = 0; idx < len; idx += 1) {
connDesc = this._connectionWaitingListByDstGMEID[gmeDstId][gmeSrcId][idx];
if (connDesc[0] === connType) {
if (connType === MetaRelations.META_RELATIONS.POINTER ||
connType === MetaRelations.META_RELATIONS.SET) {
if (pointerOrSetName &&
pointerOrSetName !== '' &&
connDesc[1].name === pointerOrSetName) {
connDesc[1] = connTexts;
}
} else {
connDesc[1] = connTexts;
}
}
}
} else if (this._GMENodes.indexOf(gmeSrcId) === -1 && this._GMENodes.indexOf(gmeDstId) !== -1) {
//#2 - the source object is missing from the screen
len = this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId].length;
for (idx = 0; idx < len; idx += 1) {
connDesc = this._connectionWaitingListBySrcGMEID[gmeSrcId][gmeDstId][idx];
if (connDesc[0] === connType) {
if (connType === MetaRelations.META_RELATIONS.POINTER ||
connType === MetaRelations.META_RELATIONS.SET) {
if (pointerOrSetName &&
pointerOrSetName !== '' &&
connDesc[1].name === pointerOrSetName) {
connDesc[1] = connTexts;
}
} else {
connDesc[1] = connTexts;
}
}
}
} else {
//#3 - both gmeSrcId and gmeDstId is missing from the screen
}
}
};
/*****************************************************************************/
/* END OF --- UPDATE CONNECTION TEXT */
/*****************************************************************************/
/**************************************************************************/
/* HANDLE OBJECT UPDATE --- DISPLAY IT WITH ALL THE POINTERS / SETS / ETC */
/**************************************************************************/
MetaEditorControl.prototype._processNodeUpdate = function (gmeID) {
var nodeObj = this._client.getNode(gmeID),
ownJsonMeta = nodeObj.getOwnJsonMeta(),
componentID,
decClass,
objDesc = {};
if (this._GMEID2ComponentID.hasOwnProperty(gmeID)) {
componentID = this._GMEID2ComponentID[gmeID];
decClass = this._client.decoratorManager.getDecoratorForWidget(META_DECORATOR, WIDGET_NAME);
objDesc.decoratorClass = decClass;
objDesc.preferencesHelper = PreferencesHelper.getPreferences([{
containerID: this.metaAspectContainerNodeID,
setID: MetaEditorConstants.META_ASPECT_SET_NAME
}]);
this.diagramDesigner.updateDesignerItem(componentID, objDesc);
// Update relation connections
this._processNodeMetaContainment(gmeID, ownJsonMeta.children || {items: []});
this._processNodeMetaPointers(gmeID, ownJsonMeta.pointers || {});
this._processNodeMetaInheritance(gmeID, nodeObj.getBaseId());
this._processNodeMixins(gmeID, ownJsonMeta.mixins || []);
}
};
/**************************************************************************/
/* END OF --- HANDLE OBJECT UPDATE */
/**************************************************************************/
/***********************************************************************************/
/* DISPLAY META CONTAINMENT RELATIONS AS A CONNECTION FROM CONTAINER TO CONTAINED */
/***********************************************************************************/
MetaEditorControl.prototype._processNodeMetaContainment = function (gmeID, children) {
// Example:
// children: {
// items: [ "/1", "/c" ],
// minItems: [ -1, -1 ],
// maxItems: [ -1, -1 ]
// }
var self = this,
sameCnt = 0;
if (this._nodeMetaContainment[gmeID] &&
JSON.stringify(this._nodeMetaContainment[gmeID]) === JSON.stringify(children)) {
return;
}
this._nodeMetaContainment[gmeID] = this._nodeMetaContainment[gmeID] || {items: []};
//compute updated and added connections
children.items.forEach(function (target, idx) {
var prevIdx = self._nodeMetaContainment[gmeID].items.indexOf(target);
if (prevIdx > -1) {
sameCnt += 1;
// Potential Update
if (self._nodeMetaContainment[gmeID].minItems[prevIdx] !== children.minItems[idx] ||
self._nodeMetaContainment[gmeID].maxItems[prevIdx] !== children.maxItems[idx]) {
self._updateConnectionText(gmeID, target, MetaRelations.META_RELATIONS.CONTAINMENT, {
dstText: getMultiplicityText(children.minItems[idx], children.maxItems[idx]),
dstTextEdit: true
});
}
} else {
// Added
self._createConnection(gmeID, target, MetaRelations.META_RELATIONS.CONTAINMENT, {
dstText: getMultiplicityText(children.minItems[idx], children.maxItems[idx]),
dstTextEdit: true
});
}
});
if (sameCnt < self._nodeMetaContainment[gmeID].items.length) {
_.difference(self._nodeMetaContainment[gmeID].items, children.items).forEach(function (target) {
self._removeConnection(gmeID, target, MetaRelations.META_RELATIONS.CONTAINMENT);
});
}
this._nodeMetaContainment[gmeID] = children;
};
/**********************************************************************************************/
/* END OF --- DISPLAY META CONTAINMENT RELATIONS AS A CONNECTION FROM CONTAINER TO CONTAINED */
/**********************************************************************************************/
/*******************************************************************************/
/* DISPLAY META POINTER RELATIONS AS A CONNECTION FROM CONTAINER TO CONTAINED */
/*******************************************************************************/
MetaEditorControl.prototype._processNodeMetaPointers = function (gmeID, pointers) {
// Example:
// pointers = {
// ptr: {
// min: 1,
// max: 1,
// items: [ "/1" ],
// minItems: [ -1 ],
// maxItems: [ 1 ]
// },
// set: {
// min: -1,
// max: -1,
// items: [ "/c" ],
// minItems: [ -1 ],
// maxItems: [ -1 ]
// }
// },
var self = this,
samePtrCnt = 0;
if (this._nodeMetaPointers[gmeID] &&
JSON.stringify(this._nodeMetaPointers[gmeID]) === JSON.stringify(pointers)) {
return;
}
this._nodeMetaPointers[gmeID] = this._nodeMetaPointers[gmeID] || {};
function createPointerOrSetConnection(ptrName, ptrDesc, target, idx) {
if (isOneToOnePointer(ptrDesc)) {
self._createConnection(gmeID, target, MetaRelations.META_RELATIONS.POINTER, {
name: ptrName
});
} else {
self._createConnection(gmeID, target, MetaRelations.META_RELATIONS.SET, {
name: ptrName,
dstText: getMultiplicityText(pointers[ptrName].minItems[idx],
pointers[ptrName].maxItems[idx]),
dstTextEdit: true
});
}
}
function removePointerOrSetConnection(ptrName, ptrDesc, target) {
if (isOneToOnePointer(ptrDesc)) {
self._removeConnection(gmeID, target, MetaRelations.META_RELATIONS.POINTER, ptrName);
} else {
self._removeConnection(gmeID, target, MetaRelations.META_RELATIONS.SET, ptrName);
}
}
Object.keys(pointers)
.forEach(function (ptrName) {
var newPtrDesc = pointers[ptrName],
sameTargetCnt,
oldPtrDesc;
if (self._nodeMetaPointers[gmeID][ptrName]) {
samePtrCnt += 1;
oldPtrDesc = self._nodeMetaPointers[gmeID][ptrName];
if (isOneToOnePointer(newPtrDesc) === isOneToOnePointer(oldPtrDesc)) {
sameTargetCnt = 0;
newPtrDesc.items.forEach(function (target, idx) {
var prevIdx = oldPtrDesc.items.indexOf(target);
if (prevIdx > -1) {
sameTargetCnt += 1;
// Potential text update for set
if (isOneToOnePointer(newPtrDesc) === false &&
(oldPtrDesc.minItems[prevIdx] !== newPtrDesc.minItems[idx] ||
oldPtrDesc.maxItems[prevIdx] !== newPtrDesc.maxItems[idx])) {
self._updateConnectionText(gmeID, target, MetaRelations.META_RELATIONS.SET, {
name: ptrName,
dstText: getMultiplicityText(newPtrDesc.minItems[idx],
newPtrDesc.maxItems[idx]),
dstTextEdit: true
});
}
} else {
// Added
createPointerOrSetConnection(ptrName, newPtrDesc, target, idx);
}
});
if (sameTargetCnt < oldPtrDesc.items.length) {
_.difference(oldPtrDesc.items, newPtrDesc.items)