@easyquery/core
Version:
EasyQuery.JS Community core modules
1,453 lines (1,438 loc) • 224 kB
JavaScript
/*
* EasyQuery.JS Core v7.3.5
* Copyright 2025 Korzh.com
* Licensed under MIT
*
* Build time: 3/23/2025 1:44:51 PM
*/
'use strict';
var core = require('@easydata/core');
/**
* Represents a parser for format expressions in operators
*/
class FormatParser {
constructor(format) {
this.start(format);
}
/**
* Starts parsing of the format string passed in the parameter
* @param format
*/
start(format) {
this.formatStr = format;
this.pos = 0;
this.exprNum = 0;
this.tokenText = '';
}
/**
* Skips all spcaes till the beginning of next token
*/
skipSpaces() {
while (this.pos < this.formatStr.length && this.formatStr.charAt(this.pos) === ' ')
this.pos++;
}
/**
* Scans the source and gets the next token
*/
next() {
this.skipSpaces();
if (this.pos >= this.formatStr.length)
return false;
var npos = 0;
if (this.formatStr.charAt(this.pos) === '{') {
npos = this.formatStr.indexOf('}', this.pos);
if (npos < 0)
return false;
this.tokenText = this.formatStr.substring(this.pos, npos + 1);
if (this.tokenText.indexOf('{expr') === 0) {
this.token = 'expression';
this.exprNum = parseInt(this.tokenText.substring(5, this.tokenText.length));
}
this.pos = npos + 1;
}
else if (this.formatStr.charAt(this.pos) === '[' && this.pos < this.formatStr.length - 1 && this.formatStr.charAt(this.pos + 1) === '[') {
this.pos += 2;
npos = this.formatStr.indexOf(']]', this.pos);
this.token = 'operator';
this.tokenText = this.formatStr.substring(this.pos, npos);
this.pos = npos + 2;
}
else {
this.token = 'text';
var npos1 = this.formatStr.indexOf('{', this.pos);
if (npos1 < 0)
npos1 = this.formatStr.length;
var npos2 = this.formatStr.indexOf('[[', this.pos);
if (npos2 < 0)
npos2 = this.formatStr.length;
npos = Math.min(npos1, npos2);
this.tokenText = this.formatStr.substring(this.pos, npos).trim();
this.pos = npos;
}
return true;
}
/**
* Returns current token
*/
getToken() {
return {
type: this.token,
text: this.tokenText,
index: this.exprNum - 1
};
}
/**
* Parses all source string passed in constructor and returns the list of tokens
*/
parse() {
let result = [];
while (this.next()) {
result.push(this.getToken());
}
return result;
}
}
/** Represents type of linking of conditions in group (predicate).*/
exports.LinkType = void 0;
(function (LinkType) {
/**
* None of the conditions must be truth (all must be false).
* In SQL it will look like: `NOT (Condition1 OR Condition2 OR ...)`.
*/
LinkType[LinkType["None"] = 0] = "None";
/** At least one condition must be truth. In SQL they are connected by `OR` operator.*/
LinkType[LinkType["Any"] = 1] = "Any";
/**
* Not all conditions must be truth (at least one must false).
* In SQL it will look like: `NOT(Condition1 AND Condition2 AND ...)`.
*/
LinkType[LinkType["NotAll"] = 2] = "NotAll";
/** All conditions must be truth, in result SQL they are connected by `AND` operator;*/
LinkType[LinkType["All"] = 3] = "All";
})(exports.LinkType || (exports.LinkType = {}));
exports.equtils = void 0;
(function (equtils) {
/**
* Adds two paths and returns the result
* Correctly processes leading and trailing slashes
* @param path1
* @param path2
*/
function combinePath(path1, path2) {
let result = path1;
if (result != null && result.length > 0) {
if (result.charAt(result.length - 1) != '/')
result += "/";
result += path2;
}
else {
result = path2;
}
return result;
}
equtils.combinePath = combinePath;
/**
* Converts a string to a `LinkType` value
* @param str
*/
function strToLinkType(str) {
if (str == "All") {
return exports.LinkType.All;
}
else if (str == "NotAll") {
return exports.LinkType.NotAll;
}
else if (str == "Any") {
return exports.LinkType.Any;
}
else {
return exports.LinkType.None;
}
}
equtils.strToLinkType = strToLinkType;
/**
* Converts a `LinkType` value to a string
* @param type
*/
function linkTypeToStr(type) {
if (type === exports.LinkType.All) {
return "All";
}
else if (type === exports.LinkType.NotAll) {
return "NotAll";
}
else if (type == exports.LinkType.Any) {
return "Any";
}
else {
return "None";
}
}
equtils.linkTypeToStr = linkTypeToStr;
/**
* Parses the operator's format string and returns a list of tokens
* @param operator
*/
function parseOperatorFormat(operator) {
let format = core.i18n.getText('Operators', operator.id, 'format');
if (!format) {
format = core.i18n.getText('Operators', operator.id, 'displayFormat');
}
if (!format) {
format = operator.displayFormat;
}
const parser = new FormatParser(format);
return parser.parse();
}
equtils.parseOperatorFormat = parseOperatorFormat;
//----------- value converstion utils --------------
/**
* Converts a value from one DataType to another
* @param value
* @param fromDataType - the original data type
* @param toDataType - the data type we want to get
*/
function convertValue(value, fromDataType, toDataType) {
switch (toDataType) {
case core.DataType.String:
return value;
case core.DataType.Autoinc, core.DataType.Byte, core.DataType.Int32, core.DataType.Int64, core.DataType.Word:
let resInt = parseInt(value);
return isNaN(resInt) ? '' : resInt.toString();
case core.DataType.Currency, core.DataType.Float:
let resFloat = parseFloat(value);
return isNaN(resFloat) ? '' : resFloat.toString();
default:
return '';
}
}
equtils.convertValue = convertValue;
})(exports.equtils || (exports.equtils = {}));
/**
* Represents an aggregate function
*/
class AggrFunction {
/** The default constructor. */
constructor() {
this.id = "";
this.caption = "";
this.sqlExpr = "";
this.displayFormat = "";
this.appliedTypes = [];
}
/**
* Loads an aggregate function from its JSON representation object
* @param aggrFunction The JSON representation object
*/
loadFromData(aggrFunction) {
if (aggrFunction) {
this.id = aggrFunction.id;
this.caption = aggrFunction.cptn;
this.displayFormat = aggrFunction.fmt;
this.sqlExpr = aggrFunction.expr;
this.appliedTypes = aggrFunction.dtypes || this.appliedTypes;
}
}
getAppliedTypesOrDefault() {
if (this.appliedTypes.length > 0)
return this.appliedTypes;
if (this.id === 'SUM' || this.id === 'AVG') {
return [core.DataType.Autoinc, core.DataType.Byte, core.DataType.Currency, core.DataType.Float, core.DataType.Int32,
core.DataType.Int64, core.DataType.Word];
}
else if (this.id === 'MIN' || this.id === 'MAX') {
return [core.DataType.Autoinc, core.DataType.BCD, core.DataType.Byte, core.DataType.Currency, core.DataType.Date, core.DataType.DateTime, core.DataType.Time,
core.DataType.Float, core.DataType.Int32, core.DataType.Int64, core.DataType.Word];
}
return core.utils.getAllDataTypes();
}
}
/**
* Represents a value editor.
*/
class EqValueEditor extends core.ValueEditor {
/** The default constructor. */
constructor() {
super();
}
/**
* Loads value editor from its JSON representation object.
* @param data The JSON representation object.
*/
loadFromData(data) {
super.loadFromData(data);
if (data) {
if (data.sql) {
this.statement = data.sql;
}
if (data.extraParams) {
this.extraParams = data.extraParams;
}
}
}
}
/** Represents expression kinds.*/
exports.DataKind = void 0;
(function (DataKind) {
/** Represents one value of some type: one constant or one attribute (field).*/
DataKind[DataKind["Scalar"] = 0] = "Scalar";
/** The same as Scalar but represents only one constant value of some type.*/
DataKind[DataKind["Const"] = 1] = "Const";
/** The same as Scalar but represents only one attribute.*/
DataKind[DataKind["Attribute"] = 2] = "Attribute";
/** Represents a list of scalar values. */
DataKind[DataKind["List"] = 4] = "List";
/** Special expression kind which represents a sub query.*/
DataKind[DataKind["Query"] = 5] = "Query";
/** Special expression kind which represents a binding to parent column.*/
DataKind[DataKind["ParentColumn"] = 6] = "ParentColumn";
})(exports.DataKind || (exports.DataKind = {}));
/**
* Represents a logical expression or predicate, like comparisions or `LIKE` predicate.
*/
class Operator {
/** The default constructor. */
constructor() {
this.id = "";
this.expr = "";
this.constValueFormat = "{const}";
this.caption = "{Unrecognized operator}";
this.displayFormat = "{expr1} [[{unrecognized operator}]] {expr2}";
this.isRange = false;
this.caseIns = false;
this.paramCount = 2;
this.defaultOperand = new Operand();
this.appliedTypes = [];
this.operands = new Array();
}
/**
* Loads operator from its JSON representation object.
* @param model The Data Model.
* @param data The JSON representation object.
*/
loadFromData(model, data) {
if (data) {
this.id = data.id;
this.caption = data.cptn;
this.caseIns = data.caseIns;
this.isRange = data.isRange;
this.displayFormat = data.fmt;
this.paramCount = data.pcnt;
this.expr = data.expr;
this.appliedTypes = data.dtypes || this.appliedTypes;
if (data.defOperand) {
this.defaultOperand.loadFromData(model, data.defOperand);
}
if (data.editor) {
this.defaultOperand.editor = model.getEditorById(data.editor) || new EqValueEditor();
}
if (data.operands) {
for (let i = 0; i < data.operands.length; i++) {
let newOperand = new Operand();
newOperand.loadFromData(model, data.operands[i]);
if (data.editor) {
newOperand.editor = model.getEditorById(data.editor) || new EqValueEditor();
}
this.operands.push(newOperand);
}
}
}
}
}
/**
* Represents one operand in some operator's expression
*/
class Operand {
/** The default constructor. */
constructor() {
this.kind = exports.DataKind.Scalar;
this.dataType = core.DataType.Unknown;
this.editor = new EqValueEditor();
this.defValue = "";
}
/**
* Loads operand from its JSON representation object.
* @param model The Data Model.
* @param operand A plain JS object that represents the operand.
*/
loadFromData(model, operand) {
this.kind = operand.kind;
this.dataType = operand.dtype;
this.defValue = operand.val;
this.defText = operand.txt;
if (operand.editor) {
this.editor = model.getEditorById(operand.editor) || new EqValueEditor();
}
}
/**
* Copies operand from other operand.
* @param src The source operand.
*/
copyFrom(src) {
core.utils.assign(this, src);
}
}
/**
* Represents one entity.
*/
class Entity extends core.MetaEntity {
/** The default constructor. */
constructor(parent) {
super(parent);
this.useInConditions = false;
this.useInResult = false;
this.useInSorting = false;
}
/**
* Loads entity from its JSON representation object.
* @param model The Data Model.
* @param data The JSON representation object.
*/
loadFromData(model, data) {
super.loadFromData(model, data);
if (data) {
this.useInConditions = data.uic;
this.useInResult = data.uir;
this.useInSorting = data.uis;
}
}
scan(processAttribute, processEntity) {
super.scan((attr, opts) => processAttribute && processAttribute(attr, opts), (entity, opts) => processEntity && processEntity(entity, opts));
}
}
/**
* Represents entity attribute.
*/
class EntityAttr extends core.MetaEntityAttr {
/** The default constructor. */
constructor(entity) {
super(entity);
/**
* The parameters associated with this entity attribute
*/
this.params = [];
this.useInConditions = true;
this.useInResult = true;
this.useInSorting = true;
this.defaultOperator = "";
this.operators = new Array();
this.lookupAttr = "";
this.expr = "";
}
/**
* Loads entity attribute from JSON representation object.
* @param model The Data Model.
* @param data The JSON representation object.
*/
loadFromData(model, data) {
super.loadFromData(model, data);
if (data) {
this.useInConditions = data.uic;
this.useInResult = data.uir;
this.useInSorting = data.uis;
this.expr = data.sql;
this.defaultOperator = data.defOperator;
this.operators = data.ops;
if (data.udata)
this.userData = data.udata;
}
}
}
/** Represents editor tags*/
const EqEditorTag = Object.assign(Object.assign({}, core.EditorTag), {
/** SqlList tag value */
SqlList: "SQLLIST",
/** SubQuery tag value */
SubQuery: "SUBQUERY",
/** Bind to parent column tag value */
BindToParentColumn: "BINDTOPARENTCOLUMN" });
/**
* Represents a data model
*/
class DataModel extends core.MetaData {
/** The default constructor. */
constructor() {
super();
this.mainEntity = null;
this.nullAttribute = new EntityAttr(null);
this.nullOperator = new Operator();
this.operators = new Array();
this.rootEntity = new Entity();
this.aggrFunctions = new Array();
this.dateMacros = ['Today', 'Yesterday',
'Tomorrow', 'FirstDayOfMonth', 'LastDayOfMonth',
'FirstDayOfWeek', 'FirstDayOfYear', 'FirstDayOfNextWeek', 'FirstDayOfPrevMonth',
'FirstDayOfNextMonth', 'FirstDayOfNextYear', 'FirstDayOfPrevYear', 'FirstDayOfPrevWeek'];
this.timeMacros = ['Now', 'HourStart', 'Midnight', 'Noon'];
this.links = [];
}
/**
* Gets the main entity of model
* @return The main entity.
*/
getMainEntity() {
return this.mainEntity;
}
/**
* Loads data model from JSON.
* @param stringJson The JSON string.
*/
loadFromJSON(stringJson) {
let model = JSON.parse(stringJson);
this.loadFromData(model);
}
/**
* Loads data model from its JSON representation object.
* @param data The JSON representation object.
*/
loadFromData(data) {
super.loadFromData(data);
//Operators
this.operators = new Array();
if (data.operators) {
for (let i = 0; i < data.operators.length; i++) {
let newOperator = new Operator();
newOperator.loadFromData(this, data.operators[i]);
this.operators.push(newOperator);
}
}
//rootEntity
this.rootEntity.loadFromData(this, data.entroot);
//Aggr Functions
this.aggrFunctions = new Array();
if (data.aggrfuncs) {
for (let i = 0; i < data.aggrfuncs.length; i++) {
let newAggrFunc = new AggrFunction();
newAggrFunc.loadFromData(data.aggrfuncs[i]);
this.aggrFunctions.push(newAggrFunc);
}
}
}
/**
* Gets the data model object.
* @returns Tha data model.
*/
getObject() {
return this;
}
/**
* Sets data to data model.
* @param model Its JSON representation object or JSON string.
*/
setData(model) {
if (typeof model === 'string') {
this.loadFromJSON(model);
}
else {
this.loadFromData(model);
}
}
/**
* Creates entity.
* @param parent The parent entity.
* @returns The Entity.
*/
createEntity(parent) {
return new Entity(parent);
}
/**
* Creates entity attribute.
* @param parent The parent entity.
* @returns The entity attribute.
*/
createEntityAttr(parent) {
return new EntityAttr(parent);
}
/**
* Creates new EqValueEditor
* @returns An instance of EqValueEditor class
*/
createValueEditor() {
return new EqValueEditor();
}
/**
* Gets root entity of the data model.
* @returns The root entity.
*/
getRootEntity() {
return this.rootEntity;
}
/**
* Finds editor by its ID.
* @param editorId The editor ID.
* @returns The value editor or `null`.
*/
getEditorById(editorId) {
return super.getEditorById(editorId);
}
/**
* Gets entity attribute by its ID.
* This function runs through all attributes inside specified model (it's root entity and all its sub-entities).
* @param attrId The attribute ID.
* @returns The attribute or `null`.
*/
getAttributeById(attrId) {
let attr = this.getEntityAttrById(this.getRootEntity(), attrId);
if (!attr) {
return null;
}
return attr;
}
/**
* Gets entity attribute by its ID.
* This function runs through all attributes inside specified model (it's root entity and all its sub-entities).
* @param attrId The attribute ID.
* @returns The attribute or `nullAttribute`.
*/
getAttributeByIdSafe(attrId) {
const attr = this.getAttributeById(attrId);
if (!attr)
return this.nullAttribute;
return attr;
}
/**
* Checks wether attribute contains such property.
* @param attrId The attribute ID.
* @param propName The property name.
* @returns `true` if the attribute contains the property, otherwise `false`.
*/
checkAttrProperty(attrId, propName) {
let attribute = this.getAttributeById(attrId);
if (attribute) {
if (typeof attribute[propName] === 'undefined') {
throw 'No such property: ' + propName;
}
if (attribute[propName]) {
return true;
}
else if (attribute.lookupAttr) {
attrId = attribute.lookupAttr;
attribute = this.getAttributeById(attrId);
return attribute && attribute[propName];
}
else {
return false;
}
}
else
return false;
}
/**
* Gets entity attribute by its ID.
* This function runs through all attributes inside specified entity and all its sub-entities.
* @param entity
* @param attrId
* @returns The attribute or `null`.
*/
getEntityAttrById(entity, attrId) {
return super.getEntityAttrById(entity, attrId);
}
_listByEntityWithFilter(entity, filterFunc) {
let result = new Array();
let caption;
let ent = null;
if (entity.subEntities) {
let subEntityCount = entity.subEntities.length;
for (let entIdx = 0; entIdx < subEntityCount; entIdx++) {
ent = entity.subEntities[entIdx];
if (!filterFunc || filterFunc(ent, null)) {
caption = core.i18n.getText('Entities', ent.name);
if (!caption) {
caption = ent.caption;
}
let newEnt = core.utils.assign(new Entity(), { id: ent.name, text: caption, items: [], isEntity: true });
newEnt.items = this._listByEntityWithFilter(ent, filterFunc);
if (newEnt.items.length > 0)
result.push(newEnt);
}
}
}
let attr = null;
if (entity.attributes) {
let attrCount = entity.attributes.length;
for (let attrIdx = 0; attrIdx < attrCount; attrIdx++) {
attr = entity.attributes[attrIdx];
if (!filterFunc || filterFunc(entity, attr)) {
caption = core.i18n.getText('Attributes', attr.id);
if (!caption)
caption = attr.caption;
let newEnt = core.utils.assign(new Entity(), { id: attr.id, text: caption, dataType: attr.dataType });
result.push(newEnt);
}
}
}
return result;
}
_listByEntity(entity, opts, filterFunc) {
opts = opts || {};
let resultEntities = [];
let resultAttributes = [];
let caption;
let ent = null;
if (entity.subEntities) {
let subEntityCount = entity.subEntities.length;
for (let entIdx = 0; entIdx < subEntityCount; entIdx++) {
ent = entity.subEntities[entIdx];
if (!filterFunc || filterFunc(ent, null)) {
if (opts.addUIC !== false && ent.useInConditions !== false ||
opts.addUIR !== false && ent.useInResult !== false ||
opts.addUIS !== false && ent.useInSorting !== false) {
caption = core.i18n.getText('Entities', ent.name) || ent.caption;
let newEnt = core.utils.assign(new Entity(), {
id: ent.name,
text: caption,
items: [],
isEntity: true,
description: ent.description
});
let newOpts = core.utils.assign({}, opts);
newOpts.includeRootData = false;
newEnt.items = this._listByEntity(ent, newOpts, filterFunc);
if (newEnt.items.length > 0) {
resultEntities.push(newEnt);
}
}
}
}
}
let attr = null;
if (entity.attributes) {
let attrCount = entity.attributes.length;
for (let attrIdx = 0; attrIdx < attrCount; attrIdx++) {
attr = entity.attributes[attrIdx];
if (!filterFunc || filterFunc(entity, attr)) {
if (opts.addUIC !== false && attr.useInConditions !== false ||
opts.addUIR !== false && attr.useInResult !== false ||
opts.addUIS !== false && attr.useInSorting !== false) {
caption = core.i18n.getText('Attributes', attr.id) || attr.caption;
resultAttributes.push(core.utils.assign(new EntityAttr(entity), { id: attr.id, text: caption, dataType: attr.dataType, lookupAttr: attr.lookupAttr, description: attr.description }));
}
}
}
}
let sortCheck = (a, b) => {
if (a.text.toLowerCase() == b.text.toLowerCase()) {
return 0;
}
if (a.text.toLowerCase() > b.text.toLowerCase()) {
return 1;
}
return -1;
};
if (opts.sortEntities) {
resultEntities.sort(sortCheck);
resultAttributes.sort(sortCheck);
}
let result;
if (!opts.attrPlacement || opts.attrPlacement == 0) {
result = resultEntities.concat(resultAttributes);
}
else {
result = resultAttributes.concat(resultEntities);
}
if (opts.attrPlacement == 2) {
result.sort(sortCheck);
}
if (opts.includeRootData) {
caption = core.i18n.getText('Entities', entity.name);
if (!caption)
caption = entity.caption;
return { id: entity.name, text: caption, items: result };
}
else {
return result;
}
}
/**
* Clears data model.
*/
clear() {
super.clear();
this.operators = [];
this.aggrFunctions = [];
}
/**
* Add or update an operator.
* @param desc The operator descriptor.
* @returns The operator.
*/
addOrUpdateOperator(desc) {
let op = core.utils.findItemById(this.operators, desc.id);
if (!op) {
op = new Operator();
op.id = desc.id;
this.operators.push(op);
}
op.caption = desc.caption;
op.expr = desc.expr || '';
op.displayFormat = desc.format;
op.defaultOperand = new Operand();
op.defaultOperand.kind = desc.kind || exports.DataKind.Scalar;
if (desc.appliedTypes)
op.appliedTypes = desc.appliedTypes;
if (desc.paramCount > 0) {
op.paramCount = desc.paramCount;
}
this.runThroughEntities((attr, opts) => {
if (attr.operators.indexOf(op.id) < 0 && op.appliedTypes.indexOf(attr.dataType) >= 0) {
attr.operators.push(op.id);
}
});
return op;
}
/**
* Removes an operator.
* @param id The id.
* @param soft If `false` - removes operators from model and attributes. Otherwise only
* from attributes.
*/
removeOperator(id, soft = false) {
let op = core.utils.findItemById(this.operators, id);
if (op) {
if (!soft)
core.utils.removeArrayItem(this.operators, op);
this.runThroughEntities((attr, opts) => {
core.utils.removeArrayItem(attr.operators, op.id);
});
}
}
/**
* Get operators for data type.
* @param type The data type
* @return The array of operator ids.
*/
getOperatorIdsByDataType(type) {
switch (type) {
case core.DataType.String:
case core.DataType.Memo:
return 'StartsWith,EndsWith,Contains,Equal,InList,NotStartsWith,NotEndsWith,NotContains,NotEqual,NotInList,IsNull,IsNotNull'.split(',');
case core.DataType.Guid:
return 'Equal,NotEqual'.split(',');
case core.DataType.Date:
case core.DataType.DateTime:
return 'DateWithinToday,DateWithinThisWeek,DateWithinPrevWeek,DateWithinThisMonth,DateWithinPrevMonth,DateWithinThisYear,DateWithinPrevYear,DateBeforePrecise,DateAfterPrecise,DatePeriodPrecise,IsNull,IsNotNull'.split(',');
case core.DataType.Time:
return 'TimeBeforePrecise,TimeAfterPrecise,TimePeriodPrecise,IsNull,IsNotNull'.split(',');
case core.DataType.Byte:
case core.DataType.Word:
case core.DataType.Int32:
case core.DataType.Int64:
case core.DataType.Float:
case core.DataType.Currency:
case core.DataType.BCD:
case core.DataType.Autoinc:
case core.DataType.Unknown:
return 'Equal,Between,LessThan,LessOrEqual,GreaterThan,GreaterOrEqual,InList,NotEqual,NotBetween,NotInList,IsNull,IsNotNull'.split(',');
case core.DataType.Bool:
return 'IsTrue,NotTrue'.split(',');
default:
const result = [];
for (let op of this.operators)
if (op.appliedTypes.indexOf(type) >= 0)
result.push(op.id);
return result;
}
}
findAggrFunctionById(funcId) {
for (const func of this.aggrFunctions) {
if (func.id === funcId)
return func;
}
return null;
}
/**
* Finds link between two entities.
* @param entityFrom The entity `from`.
* @param entityTo The entity `to`.
* @return The link.
*/
findLink(entityFrom, entityTo) {
for (let link of this.links) {
if (link.entityFrom == entityFrom
&& link.entityTo == entityTo)
return link;
}
return null;
}
/**
* Get links with the entity.
* @param entity The entity.
* @return The link.
*/
getLinksByEntity(entity) {
const result = [];
for (let link of this.links) {
if (link.entityFrom == entity || link.entityTo == entity) {
result.push(link);
}
}
return result;
}
/**
* Gets entities tree.
* @param opts The options.
* @param filterFunc The filter function.
* Takes two parameters, Entity and EntityAttr (second parameter will be null for entities), and returns boolean (true if the corresponding entity or attribute).
* @returns The tree of the entities and their attributes according to options and the filter function
*/
getEntitiesTree(opts, filterFunc) {
return this._listByEntity(this.getRootEntity(), opts, filterFunc);
}
/**
* Gets entities tree due to filter.
* @param filterFunc The filter function.
* Takes two parameters, Entity and EntityAttr (second parameter will be null for entities), and returns boolean (true if the corresponding entity or attribute).
* @returns The tree of the entities and their attributes according to the filter function
*/
getEntitiesTreeWithFilter(filterFunc) {
return this._listByEntityWithFilter(this.getRootEntity(), filterFunc);
}
/**
* Finds full entity path by attribute
* @param attrId The attribute id.
* @param sep The separator.
* @returns The path.
*/
getFullEntityPathByAttr(attrId, sep) {
sep = sep || ' ';
return this.getEntityPathByAttr(this.getRootEntity(), attrId, sep, true);
}
/**
* Finds entity path by attribute
* @param entity The entity.
* @param attrId The attribute id.
* @param sep The separator.
* @param root The root option.
* @returns The path.
*/
getEntityPathByAttr(entity, attrId, sep, root) {
return super.getEntityPathByAttr(entity, attrId, sep, root);
}
/**
* Gets the attribute text.
* @param attr The attribute.
* @param format The format.
* @returns Formatted text.
*/
getAttributeText(attr, format) {
return super.getAttributeText(attr, format);
}
getFirstUICAttr() {
return this.getFirstUICAttrInEntity(this.getRootEntity());
}
/**
* Gets first `UIC` attribute in specified entity
* (`UIC` stands for `use in conditions` - so such attribute can be used in conditions)
* @param entity Entity object to search our attribute in.
* @returns The entity attribute or `null`
*/
getFirstUICAttrInEntity(entity) {
if (entity.useInConditions !== false) {
if (entity.attributes) {
let attrCount = entity.attributes.length;
for (let i = 0; i < attrCount; i++) {
if (entity.attributes[i].useInConditions) {
return entity.attributes[i];
}
}
}
if (entity.subEntities) {
let subEntityCount = entity.subEntities.length;
for (let i = 0; i < subEntityCount; i++) {
let result = this.getFirstUICAttrInEntity(entity.subEntities[i]);
if (result) {
return result;
}
}
}
}
return null;
}
/**
* Scans model's entity tree and calls the callback functions for each attribute and entity.
* @param processAttribute The callback function which is called for each attribute in model's entity tree.
* The processed attribute is passed in the first function parameter.
* @param processEntity The callback function which is called for each entity in tree.
* The processed entity is passed in the first function parameter.
*/
runThroughEntities(processAttribute, processEntity) {
this.getRootEntity().scan(processAttribute, processEntity);
}
/**
* Finds first attribute by filter.
* @param filterFunc The filter function. Takes EntityAttr object in parameter and returns boolean
*/
getFirstAttributeByFilter(filterFunc) {
let res = null;
this.runThroughEntities(function (attr, opts) {
if (filterFunc(attr)) {
opts.stop = true;
res = attr;
}
}, null);
return res;
}
/**
* Finds operator in model by its ID.
* This function runs through all operators inside specified model and returns the one with specified ID.
* @param operatorId An operator ID.
* @returns The operator or `null`.
*/
findOperatorById(operatorId) {
if (this.operators.length > 0) {
let opCount = this.operators.length;
for (let idx = 0; idx < opCount; idx++) {
if (this.operators[idx].id == operatorId) {
return this.operators[idx];
}
}
}
return null;
}
/**
* Finds operator in model by its ID.
* his function runs through all operators inside specified model and returns the one with specified ID.
* @param operatorId Operator ID.
* @returns The operator or special `NullOperator` object if operator is not found.
*/
getOperatorById(operatorId) {
let op = this.findOperatorById(operatorId);
if (!op)
return this.nullOperator;
return op;
}
getDefaultOperatorIdForAttr(attr) {
if (attr.defaultOperator) {
return attr.defaultOperator;
}
else if (attr.operators.length > 0) {
return attr.operators[0];
}
else {
return this.nullOperator.id;
}
}
/**
* Gets caption of the aggregate function.
* @param funcId The function id.
* @returns The caption.
*/
getAggrFunctionCaption(funcId) {
let funcTexts = core.i18n.getText('AggregateFunctions', funcId);
let funcCaption = funcTexts ? funcTexts.caption : core.i18n.getText('Aggr' + funcId.replace(' ', '') + '_Caption');
if (funcCaption) {
return funcCaption;
}
let func = core.utils.findItemById(this.aggrFunctions, funcId);
if (!func || !func.caption)
return funcId;
return func.caption;
}
/**
* Gets format of the aggregate function.
* @param funcId The function id.
* @returns The caption.
*/
getAggrFunctionFormat(funcId) {
let funcTexts = core.i18n.getText('AggregateFunctions', funcId);
let funcFormat = funcTexts ? funcTexts.displayFormat : core.i18n.getText('Aggr' + funcId.replace(' ', '') + '_Format');
if (funcFormat) {
return funcFormat;
}
let func = core.utils.findItemById(this.aggrFunctions, funcId);
if (!func || !func.displayFormat)
return '';
return func.displayFormat;
}
/**
* Gets default operator for an attribute.
* @param attr The attribute.
* @returns The operator.
*/
getDefaultOperatorForAttr(attr) {
let operatorId = this.getDefaultOperatorIdForAttr(attr);
return this.getOperatorById(operatorId);
}
/**
* Gets operand.
* @param attr The attribute.
* @param operator The operator.
* @param index The index.
* @returns The operand.
*/
getOperand(attr, operator, index) {
let defOperand = new Operand();
if (operator && operator.defaultOperand) {
defOperand.copyFrom(operator.defaultOperand);
if (!defOperand.defValue) {
defOperand.defValue = '';
}
if (!defOperand.defText) {
defOperand.defText = '';
}
}
else {
defOperand.kind = exports.DataKind.Scalar;
defOperand.dataType = attr.dataType;
defOperand.defValue = '';
defOperand.defText = '';
defOperand.editor = null;
}
let result = defOperand;
if (result.dataType === core.DataType.Unknown && attr) {
result.dataType = attr.dataType;
}
if (operator && (index >= 0)) {
if (operator.operands && operator.operands[index - 1]) {
result = core.utils.assign(result, operator.operands[index - 1]);
}
}
if (!result.editor) {
if (defOperand.editor) {
result.editor = defOperand.editor;
}
else if (defOperand.kind === exports.DataKind.Query) {
result.editor.tag = EqEditorTag.SubQuery;
}
else if (attr && attr.defaultEditor) {
result.editor = attr.defaultEditor;
}
else if (result.dataType === core.DataType.Date || result.dataType === core.DataType.DateTime
|| result.dataType === core.DataType.Time) {
result.editor.tag = EqEditorTag.DateTime;
}
else {
result.editor.tag = EqEditorTag.Edit;
}
}
return result;
}
/**
* Gets the list of aggegate functions.
* @returns AN array of aggregate functions.
*/
getAggrFunctions() {
return this.aggrFunctions;
}
/**
* Checks wether macro value is date.
* @param macroExpr The macro value.
* @retruns `true` if the macro value is date, otherwise `false`.
*/
isDateMacro(macroExpr, macroProcessor) {
const match = macroExpr.match(/\${{?(.*?)}?}/);
if (match != null) {
const macroId = match[1];
if (core.utils.indexOfArrayItem(this.dateMacros, macroId) >= 0) {
if (macroProcessor) {
macroProcessor(macroId);
}
return true;
}
}
return false;
}
/**
* Checks whether the expression passed in the parameter is an time macro.
* @param macroExpr The macro expression
* @retruns `true` if the expression is a time macro (like Now), otherwise `false`.
*/
isTimeMacro(macroExpr, macroProcessor) {
const match = macroExpr.match(/\${{?(.*?)}?}/);
if (match != null) {
const macroId = match[1];
if (core.utils.indexOfArrayItem(this.timeMacros, macroId) >= 0) {
if (macroProcessor) {
macroProcessor(macroId);
}
return true;
}
}
return false;
}
/**
* Gets date by its macro value.
* @param macroExpr The macro value.
* @returns The date or `null`
*/
getDateMacroValue(macroExpr) {
let d = new Date();
const res = this.isDateMacro(macroExpr, macroId => {
switch (macroId) {
case 'Today':
break;
case 'Yesterday':
d.setDate(d.getDate() - 1);
break;
case 'Tomorrow':
d.setDate(d.getDate() + 1);
break;
case 'FirstDayOfMonth':
d.setDate(1);
break;
case 'LastDayOfMonth':
d.setMonth(d.getMonth() + 1, 0);
break;
case 'FirstDayOfPrevWeek':
var day = d.getDay();
day = (day == 0) ? 1 : 8 - day; //We start week from Monday, but js - from Sunday
d.setDate(d.getDate() - day);
break;
case 'FirstDayOfWeek':
var day = d.getDay();
day = (day == 0) ? 6 : day - 1; //We start week from Monday, but js - from Sunday
d.setDate(d.getDate() - day);
break;
case 'FirstDayOfYear':
d.setMonth(0, 1);
break;
case 'FirstDayOfNextWeek':
var day = d.getDay();
day = (day == 0) ? 1 : 8 - day; //We start week from Monday, but js - from Sunday
d.setDate(d.getDate() + day);
break;
case 'FirstDayOfNextMonth':
d.setMonth(d.getMonth() + 1, 1);
break;
case 'FirstDayOfPrevMonth':
d.setMonth(d.getMonth() - 1, 1);
break;
case 'FirstDayOfPrevYear':
d.setFullYear(d.getFullYear() - 1, 0, 1);
break;
case 'FirstDayOfNextYear':
d.setFullYear(d.getFullYear() + 1, 0, 1);
break;
}
});
return res ? d : null;
}
/**
* Gets the date value by a macro expression of the expression itself.
* @param macroExpr The macro value.
* @returns The date or macro valu.
*/
getDateOrMacroValue(macroExpr) {
let d = this.getDateMacroValue(macroExpr);
return d ? d : macroExpr;
}
/**
* Gets time by its macro value.
* @param macroExpr The macro value.
* @returns The date or `null`.
*/
getTimeMacroValue(macroExpr) {
let d = new Date();
const res = this.isDateMacro(macroExpr, macroId => {
switch (macroId) {
case 'Now':
break;
case 'HourStart':
d.setMinutes(0, 0, 0);
break;
case 'Midnight':
d.setHours(0, 0, 0, 0);
break;
case 'Noon':
d.setHours(12, 0, 0, 0);
break;
}
});
return res ? d : null;
}
/**
* Gets a time value by macor or the macro itself.
* @param time The macro or time value.
* @returns The time value or a macro that represents a particular time.
*/
getTimeOrMacroValue(time) {
var t = this.getTimeMacroValue(time);
return t ? t : time;
}
// /**
// * Gets all date marcos.
// * @returns The array of marcos
// */
getAllDateMacros() {
return this.dateMacros;
}
/**
* Gets all time marcos.
* @returns The array of marcos
*/
getAllTimeMacros() {
return this.timeMacros;
}
}
/** Contains several constant definitions for expressions tag */
exports.ExprTag = void 0;
(function (ExprTag) {
/** Unknown expression */
ExprTag[ExprTag["Unknown"] = 0] = "Unknown";
/** Constant expression */
ExprTag[ExprTag["Constant"] = 1] = "Constant";
/** Entity attributre expression */
ExprTag[ExprTag["EntityAttribute"] = 2] = "EntityAttribute";
/** Parent entity attribute expression */
ExprTag[ExprTag["ParentEntityAttribute"] = 3] = "ParentEntityAttribute";
/** Agrregate function expression */
ExprTag[ExprTag["AggregateFunction"] = 4] = "AggregateFunction";
/** Parent column expression */
ExprTag[ExprTag["ParentColumn"] = 5] = "ParentColumn";
/** Query expression */
ExprTag[ExprTag["Query"] = 11] = "Query";
/** Custom sql expression */
ExprTag[ExprTag["CustomSql"] = 21] = "CustomSql";
})(exports.ExprTag || (exports.ExprTag = {}));
/**
* Represents expression object.
*/
class Expression {
/** The default constructor. */
constructor(parent) {
this.parent = parent;
/** The tag. */
this.tag = exports.ExprTag.Constant;
/** The data kind. */
this.kind = exports.DataKind.Scalar;
/** The data type. */
this.dataType = core.DataType.Unknown;
/** The value. */
this._value = ''; //DO NOT forget remove any
/**
* The distinct option. (e.g `SELECT DISTINCT`)
*/
this.distinct = false;
this._isDefaultValue = false;
this.getText = () => this.text || this._value;
this.args = new Array();
}
getParent() {
return this.parent;
}
getModel() {
return this.parent.getQuery().getModel();
}
/**
* Loads expression from its JSON representation object.
* @param model The Data Model.
* @param data The JSON representation object.
*/
loadFromData(model, data) {
if (data) {
this.tag = data.tag;
if (data.val) {
this._value = data.val;
this.text = data.txt;
}
else if (data.id) {
this.kind = exports.DataKind.Attribute;
this._value = data.id;
this.text = data.txt;
}
if (typeof data.dtype !== 'undefined') {
this.dataType = data.dtype;
}
if (this.tag == exports.ExprTag.EntityAttribute
|| this.tag == exports.ExprTag.ParentEntityAttribute) {
this.kind = exports.DataKind.Attribute;
//We don't load other info for EntityAttr
}
else {
if (typeof data.kind !== 'undefined') {
this.kind = data.kind;
}
if (data.query) {
this.subQuery = this.parent.getQuery().createSubQuery();
this.subQuery.loadFromDataOrJson(data.query);
}
if (typeof data.distinct !== 'undefined') {
this.distinct = data.distinct;
}
if (data.func) {
this.func = data.func;
if (data.args) {
for (let i = 0; i < data.args.length; i++) {
const arg = new Expression(this.parent);
arg.loadFromData(model, data.args[i]);
this.args.push(arg);
}
}
}
if (data.sql) {
this.sql = data.sql;
this.baseAttrId = data.baseAttrId;
}
}
}
}
/**
* Saves expression from JSON representation object.
* @returns The JSON representation object.
*/
saveToData() {
let obj = {
tag: this.tag
};
obj.dtype = this.dataType;
//Do not save other info for EntityAttr
if (this.tag == exports.ExprTag.EntityAttribute || this.tag == exports.ExprTag.ParentEntityAttribute) {
if (this._value) {
obj.id = this._value;
obj.val = this._value;
}
if (this.text) {
obj.txt = this.text;
}
//Do not save other info for EntityAttr
}
else {
if (this.subQuery) {
obj.query = this.subQuery.toJSONData();
}
if (typeof this.kind !== "undefined") {
obj.kind = this.kind;
}
if (this._value) {
obj.val = this._value;
}
if (this.text) {
obj.txt = this.text;
}
if (this.distinct) {
obj.distinct = this.distinct;
}
if (this.func) {
obj.func = this.func;
obj.args = [];
for (let i = 0; i < this.args.length; i++) {
obj.args.push(this.args[i].saveToData());
}
}
if (this.sql) {
obj.sql = this.sql;
obj.baseAttrId = this.baseAttrId;
}
}
return obj;
}
getIndex() {
return this.parent.getExpressionIndex(this);
}
get value() {
return this._value;
}
setValue(val, silent = false) {
this.setContent(val, undefined, silent);
}
setContent(val, txt, silent = false) {
let oldValue = this.value;
if (this.kind == exports.DataKind.Attribute && val) {
const attr = this.getModel().getAttributeById(val);
if (!attr) {
throw "No such entity attribute:" + val;
}
this.dataType = attr.dataType;
}
this._value = val;
if (typeof txt !== 'u