GeoKnow/Jassa-Core

View on GitHub
lib/facete/ConstraintManager.js

Summary

Maintainability
A
2 hrs
Test Coverage
var forEach = require('lodash.foreach');
var uniq = require('lodash.uniq');

var Class = require('../ext/Class');

var ExprUtils = require('../sparql/ExprUtils');
var ElementsAndExprs = require('./ElementsAndExprs');

var ArrayUtils = require('../util/ArrayUtils');

/**
 * TODO Possibly rename to constraint list
 *
 * A constraint manager is a container for ConstraintSpec objects.
 *
 * @param cefRegistry
 *            A Map<String, ConstraintElementFactory>
 */
var ConstraintManager = Class.create({
    classLabel: 'jassa.facete.ConstraintManager',

    initialize: function(constraints) {
        this.constraints = constraints || [];
    },

    clear: function() {
        ArrayUtils.clear(this.constraints);
    },

    isEmpty: function() {
        var result = this.constraints.length === 0;
        return result;
    },

    /**
     * Returns a new constraintManager with a new array of the original
     * constraints
     */
    shallowClone: function() {
        var result = new ConstraintManager(this.constraints.slice(0));
        return result;
    },

    /**
     * Yields all constraints having at least one variable bound to the
     * exact path
     *
     * Note: In general, a constraint may make use of multiple paths
     */
    getConstraintsByPath: function(path) {
        var result = [];

        var constraints = this.constraints;

        //for(var i = 0; i < constraints.length; ++i) {
        constraints.forEach(function(constraint) {

            var paths = constraint.getDeclaredPaths();

            var isPath = paths.some(function(p) {
                var tmp = p.equals(path);
                return tmp;
            });

            if(isPath) {
                result.push(constraint);
            }
        });

        return result;
    },

    getConstrainedSteps: function(path) {
            // console.log("getConstrainedSteps: ", path);
        // checkNotNull(path);

        var tmp = [];

        var steps = path.getSteps();
        var constraints = this.constraints;

        for(var i = 0; i < constraints.length; ++i) {
            var constraint = constraints[i];
            // console.log(" Constraint: " + constraint);

        var paths = constraint.getDeclaredPaths();
        // console.log(" Paths: " + paths.length + " - " + paths);

        for(var j = 0; j < paths.length; ++j) {
            var p = paths[j];
            var pSteps = p.getSteps();
            var delta = pSteps.length - steps.length;

            // console.log(" Compare: " + delta, p, path);

            var startsWith = p.startsWith(path);
            // console.log(" Startswith: " + startsWith);
                if(delta == 1 && startsWith) {
                    var step = pSteps[pSteps.length - 1];
                    tmp.push(step);
                }
            }
        }

        var result = uniq(tmp, function(step) { return '' + step; });

        // console.log("Constraint result", constraints.length,
        // result.length);

        return result;
    },

    getConstraints: function() {
        return this.constraints;
    },

    addConstraint: function(constraint) {
        this.constraints.push(constraint);
    },

    addConstraints: function(constraints) {
        var self = this;
        constraints.forEach(function(constraint) {
            self.addConstraint(constraint);
        });
    },

    // Fcuking hack because of legacy code and the lack of a standard
    // collection library...
    // TODO Make the constraints a hash set (or a list set)
    removeConstraint: function(constraint) {
        var result = false;

        var cs = this.constraints;

        var n = [];
        for(var i = 0; i < cs.length; ++i) {
            var c = cs[i];

            if(!c.equals(constraint)) {
                n.push(c);
            } else {
                result = true;
            }
        }

        this.constraints = n;
        return result;
    },

    toggleConstraint: function(constraint) {
        var wasRemoved = this.removeConstraint(constraint);
        if(!wasRemoved) {
            this.addConstraint(constraint);
        }
    },

    createElementsAndExprs: function(facetNode, excludePath) {
            // var triples = [];
        var elements = [];
        var resultExprs = [];

        var pathToExprs = {};

        var self = this;

        this.constraints.forEach(function(constraint) {
            var paths = constraint.getDeclaredPaths();

            var pathId = paths.join(' ');
//            _(paths).reduce(
//                function(memo, path) {
//                    return memo + ' ' + path;
//                }, '');

            // Check if any of the paths is excluded
            if(excludePath) {
                var skip = paths.some(function(path) {
                    // console.log("Path.equals", excludePath, path);

                    var tmp = excludePath.equals(path);
                    return tmp;
                });

                if(skip) {
                    return;
                }
            }

            paths.forEach(function(path) {
                var fn = facetNode.forPath(path);
                var tmpElements = fn.getElements();
                elements.push.apply(elements, tmpElements);
            });

            var ci = constraint.createElementsAndExprs(facetNode);

            var ciElements = ci.getElements();
            var ciExprs = ci.getExprs();

            if(ciElements) {
                elements.push.apply(elements, ciElements);
            }

            if(ciExprs && ciExprs.length > 0) {

                var exprs = pathToExprs[pathId];
                if(!exprs) {
                    exprs = [];
                    pathToExprs[pathId] = exprs;
                }

                var andExpr = ExprUtils.andify(ciExprs);
                exprs.push(andExpr);
            }
        });

        forEach(pathToExprs, function(exprs) {
            var orExpr = ExprUtils.orify(exprs);
            resultExprs.push(orExpr);
        });

        var result = new ElementsAndExprs(elements, resultExprs);

        return result;
    }
});

module.exports = ConstraintManager;