Source: transform/showDiffTransformation.js

/**
 * Constructs a ShowDiffTransformation that will re-draw all dissimilar
 * hosts (comparison based on hosts' names) and events (comparison based
 * on the event capture group) between two executions, as rhombuses.
 * 
 * @classdesc
 * 
 * <p>
 * This transformation generates a transformed model by comparing the
 * hosts of the given model with the hosts of the model in the other
 * execution. Dissimilar hosts are re-drawn as rhombuses. 

 * A comparison for processes that appear in both graphs is also made 
 * and dissimilar events in these processes are re-drawn as rhombuses
 *
 * uniqueHosts is an empty array that's populated in showDiffTransformation.compare()
 * with host names for hosts that only show up in viewL or viewR. This is used in global.js
 * to draw these unique hosts as rhombuses vs squares when they are hidden by the user
 * 
 * Similarly, uniqueEvents is an empty array that's populated in showDiffTransformation.
 * compareNodeContent() with the id of visual nodes that should be drawn with an outline 
 * of a rhombus when clicked on. This is used in controller.showDialog()
 *
 * The hiddenHosts array is passed in because this transformation also needs to compare hosts that
 * are hidden and redraw them as rhombuses if they only appear in one of viewL or viewR
 *
 * </p>
 * 
 * @constructor
 */
function ShowDiffTransformation(viewBeingComparedTo, uniqueHosts, hiddenHosts, uniqueEvents, redraw) {
    /** @private */
    this.viewBeingComparedTo = viewBeingComparedTo;
    
    /** @private */
    this.uniqueHosts = uniqueHosts;
    
    /** @private */
    this.hiddenHosts = hiddenHosts;
    
    /** @private */
    this.uniqueEvents = uniqueEvents;

    /** @private */
    this.redraw = redraw;
}

// ShowDiffTransformation extends Transformation
ShowDiffTransformation.prototype = Object.create(Transformation.prototype);
ShowDiffTransformation.prototype.constructor = ShowDiffTransformation;


/**
* Compares the hosts of the given model with the hosts of viewBeingComparedTo.
* Dissimilar hosts are re-drawn as rhombuses and hosts that appear in both models 
* have their processes compared node by node.
* 
* @param {VisualGraph} model The VisualGraph to transform
*/
ShowDiffTransformation.prototype.compare = function(model) {

    // get the underlying ModelGraph and its non-hidden hosts
    var graph = model.getGraph();
    var hosts = graph.getHosts();
    // get all of the hosts (including hidden ones) for viewBeingComparedTo
    var viewBeingComparedTo = this.viewBeingComparedTo;
    var otherHiddenHosts = viewBeingComparedTo.getTransformer().getHiddenHosts();
    var allOtherHosts = viewBeingComparedTo.getModel().getHosts().concat(otherHiddenHosts);
    
    // This for loop only checks non-hidden hosts
    for (var i = 0; i < hosts.length; i++) {
        var currHost = hosts[i];
        var head = graph.getHead(currHost);
        // check if any of the hosts in the other view match this host
        // if not, add this host to the uniqueHosts array
        if (!viewBeingComparedTo.hasHost(currHost)) {
             if (head) {
                if (this.uniqueHosts.indexOf(currHost) == -1) { 
                   this.uniqueHosts.push(currHost);
                }
                if (this.redraw) {
                    var visualNode = model.getVisualNodeByNode(head);
                    // re-draw the host as a rhombus
                    visualNode.drawHostAsRhombus();
                }
             }
        // if the other view also has this host and it's not hidden,
        // compare these two processes node by node - have to check
        // hidden hosts here because when you unhide a host, it might
        // not be added to the list of visible hosts yet
        } else {
            if (otherHiddenHosts.indexOf(currHost) == -1) {
               this.compareNodes(model, currHost);
            }
        }
    }

    // Add any hidden hosts that are unique to the uniqueHosts array
    for (i = 0; i < this.hiddenHosts.length; i++) {
        var hh = this.hiddenHosts[i];
        // Have to check all hosts here because sometimes when a host
        // is hidden, it isn't added to the list of hidden hosts before
        // a comparison is made so it'll be seen as unique when it's not
        if (allOtherHosts.indexOf(hh) == -1) {
            if (this.uniqueHosts.indexOf(hh) == -1) { 
                this.uniqueHosts.push(hh);
            }
        }       
    }
};

/**
 * Compares events in processes with the same host
 *
 * @param {VisualGraph} model The VisualGraph to transform
 * @param {String} host A host that's common to both executions
 *
 */
ShowDiffTransformation.prototype.compareNodes = function(model, host) {
    
    // get the starting nodes for the two graphs being compared
    var head = model.getGraph().getHead(host);
    var otherHead = this.viewBeingComparedTo.getModel().getHead(host);
        
    var next = head.getNext();
    var otherNext = otherHead.getNext();
    this.compareNodeContent(model, next, otherNext);
}

/**
  * Compares processes node by node to find events that are dissimilar. Nodes are
  * compared by the event capture group and different nodes are re-drawn as a rhombus
  * 
  * @param {VisualGraph} model The VisualGraph to transform
  * @param {ModelNode} next The first non-start node in this graph
  * @param {ModelNode} otherNext The first non-start node in the graph of the other execution
  */

ShowDiffTransformation.prototype.compareNodeContent = function(model, next, otherNext) {
    
    var otherNextCopy = otherNext;  
    while (!next.isTail()) {
        var logEvents = next.getLogEvents();
        
        for (var i = 0; i < logEvents.length; i++) {            
            var text = logEvents[i].getText();
            var match = false;
            
            while (!otherNext.isTail()) {
                var otherLogEvents = otherNext.getLogEvents();
                for (var j = 0; j < otherLogEvents.length; j++) {
                    var otherText = otherLogEvents[j].getText();
                    if (text == otherText) { 
                       match = true; 
                       break; 
                    }
                }
                if (match) { break; }
                else { otherNext = otherNext.getNext(); }
            }
            
            if (!match) {
                var visualNode = model.getVisualNodeByNode(next);
                // keep track of unique events so that they're drawn differently when clicked on
                this.uniqueEvents.push(visualNode.getId());
                if (this.redraw) {
                    // re-draw the node as a rhombus
                    if (!visualNode.isCollapsed()) { 
                        visualNode.drawEventAsRhombus(7,9); 
                    } else {
                        // if the node is collapsed, draw a bigger rhombus with the number of collapsed nodes displayed inside
                        visualNode.drawEventAsRhombus(15,17);
                        visualNode.setLabel(visualNode.getNode().getLogEvents().length); 
                    }
                }
            }
            otherNext = otherNextCopy;
        }               
        next = next.getNext();
    }
}

/**
 * Performs the transformation on the given VisualGraph. The VisualGraph and its
 * underlying Graph are modified in place
 * 
 * @param {VisualGraph} model The VisualGraph to transform
 */
ShowDiffTransformation.prototype.transform = function(model) {
    this.compare(model);
};