diff options
| author | Mathieu Duckerts-Antoine <dam@odoo.com> | 2018-06-22 16:55:40 +0200 |
|---|---|---|
| committer | Adrien Dieudonne <adr@odoo.com> | 2018-07-06 14:09:09 +0200 |
| commit | 5d3f42a9ad3762b4871cbb4687276a530ffff8f7 (patch) | |
| tree | 34e42be34b304946f99a053c8bac79f58b02fe7e /addons/web/static/src | |
| parent | 0194d85b67c38701befa7d8560a211358fcf79e9 (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.js | 101 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_view.js | 2 |
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), }); } }); |
