webgme-bip
Version:
Design studio for component-based modeling with BIP.
409 lines (346 loc) • 15.6 kB
JavaScript
/*globals define, WebGMEGlobal*/
/*jshint browser: true*/
/**
* @author kecso / https://github.com/kecso
*/
/**
* Generated by VisualizerGenerator 1.7.0 from webgme on Wed Apr 12 2017 11:41:59 GMT-0500 (Central Daylight Time).
*/
define([
'js/Constants',
'q',
'common/util/ejs',
'bipsrc/templates/ejsCache',
'bipsrc/parsers/javaExtra',
'bipsrc/util/utils',
'bipsrc/bower_components/pegjs/peg-0.10.0'
], function (CONSTANTS,
Q,
ejs,
ejsCache,
javaParser,
utils,
peg) {
'use strict';
var BIPCodeEditorControl;
BIPCodeEditorControl = function (options) {
this._logger = options.logger.fork('Control');
this._client = options.client;
this._widget = options.widget;
this._currentNodeId = null;
this._gatheringSegments = false;
this._missedEvents = false;
this._territory = null;
this._segmentInfo = {};
this._UID = null;
this._initialize();
this._logger.debug('ctor finished');
};
BIPCodeEditorControl.prototype._initialize = function () {
var self = this;
this._UID = this._client.addUI(self, function (events) {
self._eventCallback(events);
});
this._initWidgetEventHandlers();
};
BIPCodeEditorControl.prototype._initWidgetEventHandlers = function () {
var self = this;
this._widget.onSave = function (segmentedDocumentObject) {
// console.log(segmentedDocumentObject);
self._SaveDocument(segmentedDocumentObject);
};
};
/* * * * * * * * Visualizer content update callbacks * * * * * * * */
// One major concept here is with managing the territory. The territory
// defines the parts of the project that the visualizer is interested in
// (this allows the browser to then only load those relevant parts).
BIPCodeEditorControl.prototype.selectedObjectChanged = function (nodeId) {
var node = this._client.getNode(nodeId),
typeId = node !== null ? node.getMetaTypeId() : null;
if (typeof nodeId === 'string' &&
this._currentNodeId !== nodeId &&
typeof typeId === 'string' &&
this._client.getNode(typeId).getAttribute('name') === 'ComponentType') {
//we have a viable component type so let us change things
this._currentNodeId = nodeId;
this._selfPatterns = {};
this._selfPatterns[nodeId] = {children: 1};
this._client.updateTerritory(this._UID, this._selfPatterns);
} else {
this._logger.info('received unwanted object change event');
}
};
BIPCodeEditorControl.prototype._getComponentTypeModel = function (cTypeId) {
var self = this,
deferred = Q.defer(),
model = {},
context;
Q.ninvoke(self._client, 'getCoreInstance', {})
.then(function (context_) {
context = context_;
return context.core.loadByPath(context.rootNode, cTypeId);
})
.then(function (componentType) {
if (componentType) {
return utils.getModelOfComponentType(context.core, componentType);
}
else {
throw new Error('Component was removed!');
}
})
.then(deferred.resolve)
.catch(deferred.reject);
return deferred.promise;
};
BIPCodeEditorControl.prototype._getSegmentOffset = function (segmentedDocument, segmentId) {
var compiledText = '',
index = 0,
sId = segmentedDocument.composition[0] || null;
while (sId !== null && sId !== segmentId && index < segmentedDocument.composition.length) {
compiledText += segmentedDocument.segments[sId].value + '\n';
sId = segmentedDocument.composition[++index];
}
return compiledText.split('\n').length;
};
BIPCodeEditorControl.prototype._buildSegmentInfo = function (nodeId) {
var self = this,
deferred = Q.defer(),
model,
segmentId,
guardNames = [],
guardExpressionParser,
addSegment = function (segmentId, segmentPath, segmentModel, readonly) {
var fullSegmentId = segmentId + '*' + segmentPath;
segmentedDocument.composition.push(fullSegmentId);
segmentedDocument.segments[fullSegmentId] = {
value: ejs.render(ejsCache.componentType[segmentId], segmentModel),
options: {readonly: readonly === true}
};
return fullSegmentId;
},
segmentedDocument = {composition: [], segments: {}, errors: []};
self._getComponentTypeModel(nodeId)
.then(function (model_) {
var i,
wholeDocument = ejs.render(ejsCache.componentType.complete, model_),
parseResult;
model = model_;
parseResult = javaParser.checkWholeFile(wholeDocument);
if (parseResult) {
segmentedDocument.errors.push(parseResult);
}
for (i = 0; i < model.guards.length; i += 1) {
guardNames.push(model.guards[i].name);
}
if (guardNames.length > 0) {
guardExpressionParser = peg.generate(
ejs.render(ejsCache.guardExpression, {guardNames: guardNames})
);
}
addSegment('constantImports', model.path, model, true);
addSegment('userImports', model.path, model);
addSegment('portsAnnotations', model.path, model, true);
addSegment('classStart', model.path, model, true);
addSegment('userDefinitions', model.path, model);
addSegment('classInitializations', model.path, model, true);
addSegment('userConstructors', model.path, model);
for (i = 0; i < model.transitions.length; i += 1) {
segmentId = addSegment('singleTransitionAnnotation',
model.transitions[i].path, model.transitions[i], true);
//FIXME should we allow empty string or have a constant 'true' in case of no expression
if (model.transitions[i].guard.length > 0) {
try {
parseResult = guardExpressionParser.parse(model.transitions[i].guard);
} catch (e) {
segmentedDocument.errors.push({
message: 'Guard expression should be a logical expression ' +
'that has only defined guard names as symbols.',
line: self._getSegmentOffset(segmentedDocument, segmentId)
});
}
}
segmentId = addSegment('singleTransition', model.transitions[i].path, model.transitions[i]);
parseResult = javaParser.checkForSingleFunction(
segmentedDocument.segments[segmentId].value,
null,
'public',
self._getSegmentOffset(segmentedDocument, segmentId));
if (parseResult) {
segmentedDocument.errors.push(parseResult);
}
}
for (i = 0; i < model.guards.length; i += 1) {
addSegment('singleGuardAnnotation', model.guards[i].path, model.guards[i], true);
segmentId = addSegment('singleGuard', model.guards[i].path, model.guards[i]);
parseResult = javaParser.checkForSingleFunction(
segmentedDocument.segments[segmentId].value,
'boolean',
'public',
self._getSegmentOffset(segmentedDocument, segmentId));
if (parseResult) {
segmentedDocument.errors.push(parseResult);
}
}
addSegment('classEnd', model.path, model, true);
deferred.resolve(segmentedDocument);
})
.catch(deferred.reject);
return deferred.promise;
};
BIPCodeEditorControl.prototype._SaveDocument = function (changedSegments) {
var segment,
segmentId;
this._client.startTransaction();
for (segmentId in changedSegments) {
segment = changedSegments[segmentId];
segmentId = segmentId.split('*');
switch (segmentId[0]) {
case 'userImports':
this._client.setAttribute(segmentId[1], 'forwards', segment);
break;
case 'userDefinitions':
this._client.setAttribute(segmentId[1], 'definitions', segment);
break;
case 'userConstructors':
this._client.setAttribute(segmentId[1], 'constructors', segment);
break;
case 'singleTransition':
this._client.setAttribute(segmentId[1], 'transitionMethod', segment);
break;
case 'singleGuard':
this._client.setAttribute(segmentId[1], 'guardMethod', segment);
break;
}
}
this._client.completeTransaction();
};
/* * * * * * * * Node Event Handling * * * * * * * */
//TODO needs a better change management
BIPCodeEditorControl.prototype._eventCallback = function (events) {
var self = this,
componentRemoved = false,
i;
if (events.length > 1) {
if (self._gatheringSegments) {
self._missedEvents = true;
} else {
for (i = 0; i < events.length; i += 1) {
if (events[i].eid === this._currentNodeId && events[i].etype === CONSTANTS.TERRITORY_EVENT_UNLOAD) {
componentRemoved = true;
break;
}
}
if (componentRemoved !== true) {
self._buildSegmentInfo(this._currentNodeId)
.then(function (segmentedDocumentObject) {
self._gatheringSegments = false;
if (self._missedEvents) {
self._missedEvents = false;
self._eventCallback([{
eid: self._currentNodeId,
etype: CONSTANTS.TERRITORY_EVENT_UPDATE
}, {}]);
} else {
self._widget.setSegmentedDocument(segmentedDocumentObject);
}
})
.catch(function (err) {
self._gatheringSegments = false;
self._logger.error('error during segment info build:', err);
if (self._missedEvents) {
self._missedEvents = false;
self._eventCallback([{
eid: self._currentNodeId,
etype: CONSTANTS.TERRITORY_EVENT_UPDATE
}, {}]);
}
});
}
}
}
};
BIPCodeEditorControl.prototype._stateActiveObjectChanged = function (model, activeObjectId) {
if (this._currentNodeId === activeObjectId) {
// The same node selected as before - do not trigger
} else {
this.selectedObjectChanged(activeObjectId);
}
};
/* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
BIPCodeEditorControl.prototype.destroy = function () {
this._detachClientEventListeners();
this._removeToolbarItems();
};
BIPCodeEditorControl.prototype._attachClientEventListeners = function () {
this._detachClientEventListeners();
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged, this);
};
BIPCodeEditorControl.prototype._detachClientEventListeners = function () {
WebGMEGlobal.State.off('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, this._stateActiveObjectChanged);
};
BIPCodeEditorControl.prototype.onActivate = function () {
this._attachClientEventListeners();
this._displayToolbarItems();
if (typeof this._currentNodeId === 'string') {
WebGMEGlobal.State.registerActiveObject(this._currentNodeId, {suppressVisualizerFromNode: true});
}
};
BIPCodeEditorControl.prototype.onDeactivate = function () {
this._detachClientEventListeners();
this._hideToolbarItems();
};
/* * * * * * * * * * Updating the toolbar * * * * * * * * * */
BIPCodeEditorControl.prototype._displayToolbarItems = function () {
if (this._toolbarInitialized === true) {
for (var i = this._toolbarItems.length; i--;) {
this._toolbarItems[i].show();
}
} else {
this._initializeToolbar();
}
};
BIPCodeEditorControl.prototype._hideToolbarItems = function () {
if (this._toolbarInitialized === true) {
for (var i = this._toolbarItems.length; i--;) {
this._toolbarItems[i].hide();
}
}
};
BIPCodeEditorControl.prototype._removeToolbarItems = function () {
if (this._toolbarInitialized === true) {
for (var i = this._toolbarItems.length; i--;) {
this._toolbarItems[i].destroy();
}
}
};
BIPCodeEditorControl.prototype._initializeToolbar = function () {
// var self = this,
// toolBar = WebGMEGlobal.Toolbar;
this._toolbarItems = [];
// this._toolbarItems.push(toolBar.addSeparator());
//
// /************** Go to hierarchical parent button ****************/
// this.$btnModelHierarchyUp = toolBar.addButton({
// title: 'Go to parent',
// icon: 'glyphicon glyphicon-circle-arrow-up',
// clickFn: function (/*data*/) {
// WebGMEGlobal.State.registerActiveObject(self._currentNodeParentId);
// }
// });
// this._toolbarItems.push(this.$btnModelHierarchyUp);
// this.$btnModelHierarchyUp.hide();
//
// /************** Checkbox example *******************/
//
// this.$cbShowConnection = toolBar.addCheckBox({
// title: 'toggle checkbox',
// icon: 'gme icon-gme_diagonal-arrow',
// checkChangedFn: function (data, checked) {
// self._logger.debug('Checkbox has been clicked!');
// }
// });
// this._toolbarItems.push(this.$cbShowConnection);
this._toolbarInitialized = true;
};
return BIPCodeEditorControl;
});