/**
* Constructs a Transformer
*
* @classdesc
*
* <p>
* A Transformer is responsible for transforming a {@link VisualGraph} and the
* underlying {@link ModelGraph}. Transformer exposes methods such as
* {@link Transformer#hideHost} that allow other classes to specify how this
* transformer should transform graphs. Internally, it manages a bunch of
* {@link Transformation}s to achieve the desired effect.
* </p>
*
* <p>
* Shiviz {@link Transformation}s can interact with each other in complex ways.
* For this reason, Transformer does not expose Transformations to outside
* classes, but instead acts as a black box that can be used to transform graphs
* </p>
*
* <p>
* Typical usage for this class involves invoking methods such as
* {@link Transformer#collapseNode} to specifiy how the graph should be
* transformed and then invoking the {@link Transformer#transform} method. Note
* that no graphs or visual graphs are actually modified until the transform
* method is called.
* </p>
*
* @constructor
*/
function Transformer() {
/** @private */
this.transformations = [];
/** @private */
this.hostToHidingTransform = {};
/** @private */
this.viewToDiffTransform = {};
/** @private */
this.collapseSequentialNodesTransformation = new CollapseSequentialNodesTransformation(2);
/** @private */
this.highlightHostTransformation = new HighlightHostTransformation();
/** @private */
this.highlightHostToIndex = {};
/** @private */
this.highlightMotifTransformation = null;
/** @private */
this.hiddenHosts = [];
/** @private */
this.uniqueHosts = [];
/** @private */
this.uniqueEvents = [];
/** @private */
this.highlighted = null;
}
/**
* Sets this transformer to hide the specified host. If the specified host is
* already set to be hidden, this method does nothing.
*
* @param {String} host The host that is to be hidden
* @see {@link HideHostTransformation}
*/
Transformer.prototype.hideHost = function(host) {
if (this.hostToHidingTransform[host]) {
return;
}
var trans = new HideHostTransformation(host);
this.hostToHidingTransform[host] = trans;
this.transformations.push(trans);
};
/**
* <p>
* Unsets this transformer to hide the specified host. If the specified host is
* not currently set to be hidden, this method does nothing.
* </p>
*
* <p>
* A host could've been hidden either explicitly with a call to
* {@link Transformer#hideHost} or implicitly when a host is highlighted. In the
* latter case, all highlighted nodes will be unhighlighted as well.
* </p>
*
* @param {String} host The host that is no longer to be hidden
* @see {@link HideHostTransformation}
*/
Transformer.prototype.unhideHost = function(host) {
var trans = this.hostToHidingTransform[host];
if (trans) {
var index = this.transformations.indexOf(trans);
this.transformations.splice(index, 1);
delete this.hostToHidingTransform[host];
}
else if (this.highlightHostTransformation.isHidden(host)) {
this.highlightHostTransformation.clearHosts();
}
};
/**
* Gets the hosts that are explicitly specified to be hidden. Note that a hosts
* that doesn't actually exist can be specified to be hidden. In addition, there
* are hosts that may be implicitly hidden by other transformations. These two
* facts mean that the returned list of hosts may be different from the hosts
* that are actually hidden.
*
* @returns {Array<String>} the hosts specified to be hidden
* @see {@link HideHostTransformation}
*/
Transformer.prototype.getSpecifiedHiddenHosts = function() {
return Object.keys(this.hostToHidingTransform);
};
/**
* Get all of the hosts hidden by this transformer by the last invocation of
* {@link Transformer#transform}. If the transform method has never been
* called, this method returns an empty array.
*
* @returns {Array<String>} the hosts that have been hidden by this
* transformer.
*/
Transformer.prototype.getHiddenHosts = function() {
return this.hiddenHosts.slice();
};
/**
* Get all of the unique hosts hidden by this transformer by the last invocation of
* {@link Transformer#transform}. If the transform method has never been
* called, this method returns an empty array.
*
* @returns {Array<String>} the unique hosts that have been hidden by this
* transformer.
*/
Transformer.prototype.getUniqueHosts = function() {
return this.uniqueHosts.slice();
};
/**
* Get all of the unique events transformed by this transformer by the last invocation of
* {@link Transformer#transform}. If the transform method has never been
* called, this method returns an empty array.
*
* @returns {Array<String>} the unique events that have been transformed by this
* transformer.
*/
Transformer.prototype.getUniqueEvents = function() {
return this.uniqueEvents.slice();
};
/**
* Sets this transformer to highlight the specified host.
*
* @param {String} host The host to be highlighted
* @param {Boolean} def Whether the transformation to remove is a default
* transformation.
* @see {@link HighlightHostTransformation}
*/
Transformer.prototype.highlightHost = function(host) {
this.highlightHostTransformation.addHost(host);
this.highlightHostToIndex[host] = this.transformations.length;
};
/**
* Unsets this transformer to highlight the specified host.
*
* @param {String} host The host that is no longer to be highlighted
* @see {@link HighlightHostTransformation}
*/
Transformer.prototype.unhighlighHost = function(host) {
this.highlightHostTransformation.removeHost(host);
delete this.highlightHostToIndex[host];
};
/**
* Toggles highlighting of the specified host.
*
* @param {String} host
* @see {@link HighlightHostTransformation}
*/
Transformer.prototype.toggleHighlightHost = function(host) {
if (this.highlightHostTransformation.isHighlighted(host)) {
this.unhighlighHost(host);
}
else {
this.highlightHost(host);
}
};
/**
* Sets this transformer to collapse the node and its group into one.
* Intuitively, the node's group are the nodes surrounding the argument that
* have no family.
*
* @param {ModelNode} node
* @see {@link CollapseSequentialNodesTransformation}
*/
Transformer.prototype.collapseNode = function(node) {
this.collapseSequentialNodesTransformation.removeExemption(node);
};
/**
* Sets this transformer to not collapse the node or any of the nodes in its
* group. Intuitively, the node's group are the nodes surrounding the argument
* that have no family.
*
* @param {ModelNode} node
* @see {@link CollapseSequentialNodesTransformation}
*/
Transformer.prototype.uncollapseNode = function(node) {
this.collapseSequentialNodesTransformation.addExemption(node);
};
/**
* Toggles collapsing of the node
*
* @param {ModelNode} node
* @see {@link CollapseSequentialNodesTransformation}
*/
Transformer.prototype.toggleCollapseNode = function(node) {
this.collapseSequentialNodesTransformation.toggleExemption(node);
};
/**
* Sets this transformer to highlight a motif found by a MotifFinder. Only one
* motif can be highlighted at a time, thus if there is already a motif set to
* be highlighted, that one is replaced.
*
* @param {MotifFinder} motifFinder The motif finder that specifies which nodes
* and edges are to be highlighted
* @param {Boolean} ignoreEdges edges will not be highlighted if true
* @see {@link HighlightMotifTransformation}
*/
Transformer.prototype.highlightMotif = function(motifFinder, ignoreEdges) {
this.highlightMotifTransformation = new HighlightMotifTransformation(motifFinder, ignoreEdges);
};
/**
* Sets this transformer to not highlight motifs.
*
* @see {@link HighlightMotifTransformation}
*/
Transformer.prototype.unhighlightMotif = function() {
this.highlightMotifTransformation = null;
};
/**
* Determines if a motif is currently set to be highlighted.
*
* @returns {Boolean} True if a motif is currently set to be highlighted
* @see {@link HighlightMotifTransformation}
*/
Transformer.prototype.hasHighlightedMotif = function() {
return this.highlightMotifTransformation != null;
};
/**
* Returns the motif group that represents the highlighted elements from the
* last invocation of {@link Transformer#transform}. If transform has yet to be
* called, this method returns null
*
* @returns {MotifGroup}
* @see {@link HighlightMotifTransformation}
*/
Transformer.prototype.getHighlightedMotif = function() {
return this.highlighted;
};
/**
* Returns the highlightMotifTransformation for this transformer.
* If highlightMotif has not been called, this method returns null
*
* @returns {HighlightMotifTransformation}
*/
Transformer.prototype.getHighlightMotifTransformation = function() {
return this.highlightMotifTransformation;
}
Transformer.prototype.setHighlightMotifTransformation = function(hmt) {
this.highlightMotifTransformation = hmt;
}
/**
* Sets this transformer to highlight different hosts in the View
* this transformer belongs to and the given View passed to the function
*/
Transformer.prototype.showDiff = function(view) {
if (this.viewToDiffTransform[view]) return;
this.uniqueHosts = [];
this.uniqueEvents = [];
var trans = new ShowDiffTransformation(view, this.uniqueHosts, this.hiddenHosts, this.uniqueEvents, true);
this.viewToDiffTransform[view] = trans;
this.transformations.push(trans);
}
/**
* Sets this transformer to not highlight different hosts
*/
Transformer.prototype.hideDiff = function(view) {
var trans = this.viewToDiffTransform[view];
if (trans) {
var index = this.transformations.indexOf(trans);
this.transformations.splice(index, 1);
delete this.viewToDiffTransform[view];
}
}
/**
* Transforms the specified {@link VisualGraph} and the underlying
* {@link ModelGraph} based on the settings of this transformer. Note that this
* method is solely responsible for modifying visual and model graphs
*/
Transformer.prototype.transform = function(visualModel) {
var originalHosts = visualModel.getHosts();
// get the underlying modelGraph
var graph = visualModel.getGraph();
this.collapseSequentialNodesTransformation.transform(visualModel);
var maxIndex = 0;
for (var key in this.highlightHostToIndex) {
maxIndex = Math.max(maxIndex, this.highlightHostToIndex[key]);
}
for (var i = 0; i < maxIndex; i++) {
var trans = this.transformations[i];
trans.transform(visualModel);
}
this.highlightHostTransformation.transform(visualModel);
for (var i = maxIndex; i < this.transformations.length; i++) {
var trans = this.transformations[i];
trans.transform(visualModel);
}
if (this.highlightMotifTransformation != null) {
this.highlightMotifTransformation.transform(visualModel);
this.highlighted = this.highlightMotifTransformation.getHighlighted();
}
var hidden = {};
for (var i = 0; i < originalHosts.length; i++) {
var host = originalHosts[i];
if (this.hostToHidingTransform[host]) {
hidden[host] = true;
}
}
this.highlightHostTransformation.getHiddenHosts().forEach(function(host) {
hidden[host] = true;
});
this.hiddenHosts = Object.keys(hidden);
};