import { CompositeFilterDescriptor, FilterDescriptor, isCompositeFilterDescriptor } from "./filter-descriptor.interface"; import { Predicate, Preprocessors } from "../common.interfaces"; import { getter } from "../accessor"; import { isFunction, isPresent, isDate, isString, isBlank, isNumeric, isArray } from "../utils"; const logic = { "or": { concat: (acc, fn) => a => acc(a) || fn(a), identity: () => false }, "and": { concat: (acc, fn) => a => acc(a) && fn(a), identity: () => true } }; const operatorsMap = { contains: (a, b) => (a || "").indexOf(b) >= 0, doesnotcontain: (a, b) => (a || "").indexOf(b) === -1, doesnotendwith: (a, b) => (a || "").indexOf(b, (a || "").length - (b || "").length) < 0, doesnotstartwith: (a: string, b: string): boolean => (a || "").lastIndexOf(b, 0) === -1, endswith: (a: string, b: string): boolean => (a || "").indexOf(b, (a || "").length - (b || "").length) >= 0, eq: (a, b) => a === b, gt: (a: T, b: T): boolean => a > b, gte: (a: T, b: T): boolean => a >= b, isempty: (a: any) => isBlank(a) || a === '', isnotempty: (a: any) => isPresent(a) && a !== '', isnotnull: (a: any) => isPresent(a), isnull: (a: any) => isBlank(a), lt: (a: T, b: T): boolean => a < b, lte: (a: T, b: T): boolean => a <= b, neq: (a: T, b: T): boolean => a != b, // tslint:disable-line:triple-equals startswith: (a: string, b: string): boolean => (a || "").lastIndexOf(b, 0) === 0, in: (a: T, b: T[]): boolean => b.includes(a), notin: (a: T, b: T[]): boolean => !b.includes(a), isinteger: (a: any) => isNumeric(a) && Math.round(a) === a, isfractional: (a: any) => isNumeric(a) && Math.round(a) !== a }; const dateRegExp = /^\/Date\((.*?)\)\/$/; const convertValue = (value, ignoreCase) => { if (value != null && isString(value)) { const date = dateRegExp.exec(value); if (date) { return new Date(+date[1]).getTime(); } else if (ignoreCase) { return value.toLowerCase(); } } else if (value != null && isDate(value)) { return value.getTime(); } else if (value != null && isArray(value)) { return value.map(v => isString(v) ? v.toLowerCase() : v); } return value; }; const typedGetter = (prop, value, ignoreCase) => { if (!isPresent(value)) { return prop; } let acc = prop; if (isString(value)) { const date = dateRegExp.exec(value); if (date) { value = new Date(+date[1]); } else { acc = a => { const x = prop(a); if (typeof x === 'string' && ignoreCase) { return x.toLowerCase(); } else { return isNumeric(x) ? x + "" : x; } }; } } if (isDate(value)) { return a => { const x = acc(a); return isDate(x) ? x.getTime() : x; }; } if(isArray(value) && ignoreCase){ acc = a => { const x = prop(a); return isString(x) ? x.toLowerCase() : x; }; } return acc; }; const transformFilter = ({ field, ignoreCase, value, operator }: FilterDescriptor, preprocessors: Preprocessors = {}): Predicate => { field = !isPresent(field) ? a => a : field; ignoreCase = isPresent(ignoreCase) ? ignoreCase : true; const itemProp = typedGetter( isFunction(field) ? field : a => { const value = getter(field, true)(a); const preprocessor = preprocessors[field]; return preprocessor ? preprocessor(value) : value; }, value, ignoreCase ); value = convertValue(value, ignoreCase); const op = isFunction(operator) ? operator : operatorsMap[operator]; return a => op(itemProp(a), value, ignoreCase); }; /** * @hidden */ export const transformCompositeFilter = (filter: CompositeFilterDescriptor, preprocessors: Preprocessors = {}): Predicate => { const combiner = logic[filter.logic]; return filter.filters .filter(isPresent) .map(x => isCompositeFilterDescriptor(x) ? transformCompositeFilter(x, preprocessors) : transformFilter(x, preprocessors)) .reduce(combiner.concat, combiner.identity); };