summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorMathieu Duckerts-Antoine <dam@odoo.com>2018-06-22 16:55:40 +0200
committerAdrien Dieudonne <adr@odoo.com>2018-07-06 14:09:09 +0200
commit5d3f42a9ad3762b4871cbb4687276a530ffff8f7 (patch)
tree34e42be34b304946f99a053c8bac79f58b02fe7e /addons/web/static/src
parent0194d85b67c38701befa7d8560a211358fcf79e9 (diff)
[IMP] web: implement assemble domain utility function
In this commit, we implement a function which allow to combine a list of domains into a single normalized using a 'OR' or an 'AND' operator. We also refactor slightly py_utils.
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/core/py_utils.js101
-rw-r--r--addons/web/static/src/js/views/basic/basic_view.js2
2 files changed, 86 insertions, 17 deletions
diff --git a/addons/web/static/src/js/core/py_utils.js b/addons/web/static/src/js/core/py_utils.js
index f275302279b..3ebb79d49ae 100644
--- a/addons/web/static/src/js/core/py_utils.js
+++ b/addons/web/static/src/js/core/py_utils.js
@@ -314,6 +314,59 @@ function py_eval(expr, context) {
return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
}
+/**
+ * Assemble domains into a single domains using an 'OR' or an 'AND' operator.
+ *
+ * Note: - this function does not evaluate anything inside the domain. This is
+ * actually quite critical because this allows the manipulation of unevaluated
+ * (dynamic) domains.
+ * - this function gives a normalized domain as result,
+ * - applied on a list of length 1, it returns the domain normalized.
+ *
+ * @param {string[]} domains list of string representing domains
+ * @param {"AND" | "OR"} operator used to combine domains (default "AND")
+ * @returns {string} normalized domain
+ */
+function assembleDomains(domains, operator) {
+ var ASTs = domains.map(_getPyJSAST);
+ if (operator === "OR") {
+ operator = py.tokenize("'|'")[0];
+ } else {
+ operator = py.tokenize("'&'")[0];
+ }
+ var result = _getPyJSAST("[]");
+ var normalizedDomains = ASTs
+ .filter(function (AST) {
+ return AST.first.length > 0;
+ })
+ .map(_normalizeDomainAST);
+ if (normalizedDomains.length > 0) {
+ result.first = normalizedDomains.reduce(function (acc, ast) {
+ return acc.concat(ast.first);
+ },
+ _.times(normalizedDomains.length - 1, _.constant(operator))
+ );
+ }
+ return _formatAST(result);
+}
+/**
+ * Normalize a domain via its string representation.
+ *
+ * Note: this function does not evaluate anything inside the domain. This is
+ * actually quite critical because this allows the manipulation of unevaluated
+ * (dynamic) domains.
+ *
+ * @param {string} domain string representing a domain
+ * @returns {string} normalized domain
+ */
+function normalizeDomain (domain) {
+ return _formatAST(_normalizeDomainAST(_getPyJSAST(domain)));
+}
+
+//--------------------------------------------------------------------------
+// Private
+//--------------------------------------------------------------------------
+
// Binding power for prefix operator is not accessible in the AST generated by
// py.js, so we have to hardcode some values here
var BINDING_POWERS = {
@@ -323,6 +376,7 @@ var BINDING_POWERS = {
};
/**
+ * @private
* Convert a python AST (generated by py.js) to a string form, which should
* represent the same AST.
*
@@ -334,7 +388,7 @@ var BINDING_POWERS = {
* binding power of *.
* @returns {string}
*/
-function formatAST(ast, lbp) {
+function _formatAST(ast, lbp) {
lbp = lbp || 0;
switch (ast.id) {
// basic values
@@ -349,21 +403,21 @@ function formatAST(ast, lbp) {
case "[":
if (ast.second) {
// read a value in a dictionary: d['a']
- return formatAST(ast.first) + '[' + formatAST(ast.second) + ']';
+ return _formatAST(ast.first) + '[' + _formatAST(ast.second) + ']';
} else {
// list: [1, 2]
- var values = ast.first.map(formatAST);
+ var values = ast.first.map(_formatAST);
return '[' + values.join(', ') + ']';
}
case "{":
var keyValues = ast.first.map(function (kv) {
- return formatAST(kv[0]) + ': ' + formatAST(kv[1]);
+ return _formatAST(kv[0]) + ': ' + _formatAST(kv[1]);
});
return '{' + keyValues.join(', ') + '}';
// relations
case "=":
- return formatAST(ast.first) + ' ' + ast.id + ' ' + formatAST(ast.second);
+ return _formatAST(ast.first) + ' ' + ast.id + ' ' + _formatAST(ast.second);
// operators
case "-":
case "+":
@@ -376,7 +430,7 @@ function formatAST(ast, lbp) {
case "or":
if (ast.second) {
// infix
- var r = formatAST(ast.first, ast.lbp) + ' ' + ast.id + ' ' + formatAST(ast.second, ast.lbp);
+ var r = _formatAST(ast.first, ast.lbp) + ' ' + ast.id + ' ' + _formatAST(ast.second, ast.lbp);
if (ast.lbp < lbp) {
r = '(' + r + ')';
}
@@ -385,29 +439,42 @@ function formatAST(ast, lbp) {
// prefix
// real lbp is not accessible, it is inside a closure
var actualBP = BINDING_POWERS[ast.id] || 130;
- return ast.id + formatAST(ast.first, actualBP);
+ return ast.id + _formatAST(ast.first, actualBP);
case ".":
- return formatAST(ast.first, ast.lbp) + '.' + formatAST(ast.second);
+ return _formatAST(ast.first, ast.lbp) + '.' + _formatAST(ast.second);
case "not":
- return "not " + formatAST(ast.first);
+ return "not " + _formatAST(ast.first);
case "(comparator)":
- var operator = ast.operators[0]
- return formatAST(ast.expressions[0]) + ' ' + operator + ' ' + formatAST(ast.expressions[1]);
+ var operator = ast.operators[0];
+ return _formatAST(ast.expressions[0]) + ' ' + operator + ' ' + _formatAST(ast.expressions[1]);
// function call
case "(":
if (ast.second) {
// this is a function call: f(a, b)
- return formatAST(ast.first) + '(' + ast.second.map(formatAST).join(', ') + ')';
+ return _formatAST(ast.first) + '(' + ast.second.map(_formatAST).join(', ') + ')';
} else {
// this is a tuple
- return '(' + ast.first.map(formatAST).join(', ') + ')';
+ return '(' + ast.first.map(_formatAST).join(', ') + ')';
}
}
throw new Error("Unimplemented python construct");
}
/**
+ * @private
+ * Get the PyJs AST representing a domain starting from is string representation
+ *
+ * @param {string} domain string representing a domain
+ * @returns {PyJS AST} PyJS AST representation of domain
+ */
+function _getPyJSAST(domain) {
+ return py.parse(py.tokenize(domain));
+}
+
+/**
+ * @private
+ *
* Normalize a domain, at the level of the AST.
*
* Note: this function does not evaluate anything inside the domain. This is
@@ -417,7 +484,7 @@ function formatAST(ast, lbp) {
* @param {PyJS AST} domain valid AST representing a domain
* @returns {PyJS AST} normalized domain AST
*/
-function normalizeDomain(domain) {
+function _normalizeDomainAST(domain) {
var expected = 1;
for (var i = 0; i < domain.first.length; i++) {
var value = domain.first[i].value;
@@ -442,8 +509,10 @@ return {
eval: pyeval,
eval_domains_and_contexts: eval_domains_and_contexts,
py_eval: py_eval,
- formatAST: formatAST,
normalizeDomain: normalizeDomain,
+ assembleDomains: assembleDomains,
+ _getPyJSAST: _getPyJSAST,
+ _formatAST: _formatAST,
+ _normalizeDomainAST: _normalizeDomainAST,
};
-
});
diff --git a/addons/web/static/src/js/views/basic/basic_view.js b/addons/web/static/src/js/views/basic/basic_view.js
index cb4afe73476..7bc5e95bfc8 100644
--- a/addons/web/static/src/js/views/basic/basic_view.js
+++ b/addons/web/static/src/js/views/basic/basic_view.js
@@ -216,7 +216,7 @@ var BasicView = AbstractView.extend({
attrs.decorations = attrs.decorations || [];
attrs.decorations.push({
className: 'text-' + splitKey[1],
- expression: py.parse(py.tokenize(value))
+ expression: pyUtils._getPyJSAST(value),
});
}
});