pip-services4-expressions-node
Version:
Tokenizers, parsers and expression calculators in Node.js / ES2017
821 lines (737 loc) • 32.2 kB
text/typescript
/** @module variants */
import { Variant } from './Variant';
import { VariantType } from './VariantType';
import { IVariantOperations } from "./IVariantOperations";
/**
* Implements an abstractd variant operations manager object.
*/
export abstract class AbstractVariantOperations implements IVariantOperations {
/**
* Convert variant type to string representation
* @param value a variant type to be converted.
* @returns a string representation of the type.
*/
protected typeToString(value: VariantType): string {
switch (value) {
case VariantType.Null:
return "Null";
case VariantType.Integer:
return "Integer";
case VariantType.Long:
return "Long";
case VariantType.Float:
return "Float";
case VariantType.Double:
return "Double";
case VariantType.String:
return "String";
case VariantType.Boolean:
return "Boolean";
case VariantType.DateTime:
return "DateTime";
case VariantType.TimeSpan:
return "TimeSpan";
case VariantType.Object:
return "Object";
case VariantType.Array:
return "Array";
default:
return "Unknown";
}
}
/**
* Converts variant to specified type
* @param value A variant value to be converted.
* @param newType A type of object to be returned.
* @returns A converted Variant value.
*/
public abstract convert(value: Variant, newType: VariantType): Variant;
/**
* Performs '+' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public add(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger + value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong + value2.asLong;
return result;
case VariantType.Float:
result.asFloat = value1.asFloat + value2.asFloat;
return result;
case VariantType.Double:
result.asDouble = value1.asDouble + value2.asDouble;
return result;
case VariantType.TimeSpan:
result.asTimeSpan = value1.asTimeSpan + value2.asTimeSpan;
return result;
case VariantType.String:
result.asString = value1.asString + value2.asString;
return result;
}
throw new Error("Operation '+' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '-' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public sub(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger - value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong - value2.asLong;
return result;
case VariantType.Float:
result.asFloat = value1.asFloat - value2.asFloat;
return result;
case VariantType.Double:
result.asDouble = value1.asDouble - value2.asDouble;
return result;
case VariantType.TimeSpan:
result.asTimeSpan = value1.asTimeSpan - value2.asTimeSpan;
return result;
case VariantType.DateTime:
result.asTimeSpan = value1.asDateTime.getTime() - value2.asDateTime.getTime();
return result;
}
throw new Error("Operation '-' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '*' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public mul(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger * value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong * value2.asLong;
return result;
case VariantType.Float:
result.asFloat = value1.asFloat * value2.asFloat;
return result;
case VariantType.Double:
result.asDouble = value1.asDouble * value2.asDouble;
return result;
}
throw new Error("Operation '*' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '/' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public div(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger / value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong / value2.asLong;
return result;
case VariantType.Float:
result.asFloat = value1.asFloat / value2.asFloat;
return result;
case VariantType.Double:
result.asDouble = value1.asDouble / value2.asDouble;
return result;
}
throw new Error("Operation '/' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '%' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public mod(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger % value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong % value2.asLong;
return result;
}
throw new Error("Operation '%' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '^' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public pow(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
case VariantType.Long:
case VariantType.Float:
case VariantType.Double:
// Converts second operant to the type of the first operand.
value1 = this.convert(value1, VariantType.Double);
value2 = this.convert(value2, VariantType.Double);
result.asDouble = value1.asDouble * value2.asDouble;
return result;
}
throw new Error("Operation '^' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs AND operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public and(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger & value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong & value2.asLong;
return result;
case VariantType.Boolean:
result.asBoolean = value1.asBoolean && value2.asBoolean;
return result;
}
throw new Error("Operation AND is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs OR operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public or(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger | value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong | value2.asLong;
return result;
case VariantType.Boolean:
result.asBoolean = value1.asBoolean || value2.asBoolean;
return result;
}
throw new Error("Operation OR is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs XOR operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public xor(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger ^ value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong ^ value2.asLong;
return result;
case VariantType.Boolean:
result.asBoolean = (value1.asBoolean && !value2.asBoolean) || (!value1.asBoolean && value2.asBoolean);
return result;
}
throw new Error("Operation XOR is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '<<' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public lsh(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, VariantType.Integer);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger << value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong << value2.asInteger;
return result;
}
throw new Error("Operation '<<' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '>>' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public rsh(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, VariantType.Integer);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asInteger = value1.asInteger >> value2.asInteger;
return result;
case VariantType.Long:
result.asLong = value1.asLong >> value2.asInteger;
return result;
}
throw new Error("Operation '>>' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs NOT operation for a variant.
* @param value The operand for this operation.
* @returns A result variant object.
*/
public not(value: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value.type == VariantType.Null) {
return result;
}
// Performs operation.
switch (value.type) {
case VariantType.Integer:
result.asInteger = ~value.asInteger;
return result;
case VariantType.Long:
result.asLong = ~value.asLong;
return result;
case VariantType.Boolean:
result.asBoolean = !value.asBoolean;
return result;
}
throw new Error("Operation NOT is not supported for type " + this.typeToString(value.type));
}
/**
* Performs unary '-' operation for a variant.
* @param value The operand for this operation.
* @returns A result variant object.
*/
public negative(value: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value.type == VariantType.Null) {
return result;
}
// Performs operation.
switch (value.type) {
case VariantType.Integer:
result.asInteger = -value.asInteger;
return result;
case VariantType.Long:
result.asLong = -value.asLong;
return result;
case VariantType.Float:
result.asFloat = -value.asFloat;
return result;
case VariantType.Double:
result.asDouble = -value.asDouble;
return result;
}
throw new Error("Operation unary '-' is not supported for type " + this.typeToString(value.type));
}
/**
* Performs '=' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public equal(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null && value2.type == VariantType.Null) {
result.asBoolean = true;
return result;
}
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
result.asBoolean = false;
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asBoolean = value1.asInteger == value2.asInteger;
return result;
case VariantType.Long:
result.asBoolean = value1.asLong == value2.asLong;
return result;
case VariantType.Float:
result.asBoolean = value1.asFloat == value2.asFloat;
return result;
case VariantType.Double:
result.asBoolean = value1.asDouble == value2.asDouble;
return result;
case VariantType.String:
result.asBoolean = value1.asString == value2.asString;
return result;
case VariantType.TimeSpan:
result.asBoolean = value1.asTimeSpan == value2.asTimeSpan;
return result;
case VariantType.DateTime:
result.asBoolean = value1.asDateTime.getTime() == value2.asDateTime.getTime();
return result;
case VariantType.Boolean:
result.asBoolean = value1.asBoolean == value2.asBoolean;
return result;
case VariantType.Object:
result.asObject = value1.asObject == value2.asObject;
return result;
}
throw new Error("Operation '=' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '<>' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public notEqual(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null && value2.type == VariantType.Null) {
result.asBoolean = false;
return result;
}
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
result.asBoolean = true;
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asBoolean = value1.asInteger != value2.asInteger;
return result;
case VariantType.Long:
result.asBoolean = value1.asLong != value2.asLong;
return result;
case VariantType.Float:
result.asBoolean = value1.asFloat != value2.asFloat;
return result;
case VariantType.Double:
result.asBoolean = value1.asDouble != value2.asDouble;
return result;
case VariantType.String:
result.asBoolean = value1.asString != value2.asString;
return result;
case VariantType.TimeSpan:
result.asBoolean = value1.asTimeSpan != value2.asTimeSpan;
return result;
case VariantType.DateTime:
result.asBoolean = value1.asDateTime.getTime() != value2.asDateTime.getTime();
return result;
case VariantType.Boolean:
result.asBoolean = value1.asBoolean != value2.asBoolean;
return result;
case VariantType.Object:
result.asObject = value1.asObject != value2.asObject;
return result;
}
throw new Error("Operation '<>' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '>' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public more(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asBoolean = value1.asInteger > value2.asInteger;
return result;
case VariantType.Long:
result.asBoolean = value1.asLong > value2.asLong;
return result;
case VariantType.Float:
result.asBoolean = value1.asFloat > value2.asFloat;
return result;
case VariantType.Double:
result.asBoolean = value1.asDouble > value2.asDouble;
return result;
case VariantType.String:
result.asBoolean = value1.asString > value2.asString;
return result;
case VariantType.TimeSpan:
result.asBoolean = value1.asTimeSpan > value2.asTimeSpan;
return result;
case VariantType.DateTime:
result.asBoolean = value1.asDateTime.getTime() > value2.asDateTime.getTime();
return result;
}
throw new Error("Operation '>' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '<' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public less(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asBoolean = value1.asInteger < value2.asInteger;
return result;
case VariantType.Long:
result.asBoolean = value1.asLong < value2.asLong;
return result;
case VariantType.Float:
result.asBoolean = value1.asFloat < value2.asFloat;
return result;
case VariantType.Double:
result.asBoolean = value1.asDouble < value2.asDouble;
return result;
case VariantType.String:
result.asBoolean = value1.asString < value2.asString;
return result;
case VariantType.TimeSpan:
result.asBoolean = value1.asTimeSpan < value2.asTimeSpan;
return result;
case VariantType.DateTime:
result.asBoolean = value1.asDateTime.getTime() < value2.asDateTime.getTime();
return result;
}
throw new Error("Operation '<' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '>=' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public moreEqual(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asBoolean = value1.asInteger >= value2.asInteger;
return result;
case VariantType.Long:
result.asBoolean = value1.asLong >= value2.asLong;
return result;
case VariantType.Float:
result.asBoolean = value1.asFloat >= value2.asFloat;
return result;
case VariantType.Double:
result.asBoolean = value1.asDouble >= value2.asDouble;
return result;
case VariantType.String:
result.asBoolean = value1.asString >= value2.asString;
return result;
case VariantType.TimeSpan:
result.asBoolean = value1.asTimeSpan >= value2.asTimeSpan;
return result;
case VariantType.DateTime:
result.asBoolean = value1.asDateTime.getTime() >= value2.asDateTime.getTime();
return result;
}
throw new Error("Operation '>=' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs '<=' operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public lessEqual(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Converts second operant to the type of the first operand.
value2 = this.convert(value2, value1.type);
// Performs operation.
switch (value1.type) {
case VariantType.Integer:
result.asBoolean = value1.asInteger <= value2.asInteger;
return result;
case VariantType.Long:
result.asBoolean = value1.asLong <= value2.asLong;
return result;
case VariantType.Float:
result.asBoolean = value1.asFloat <= value2.asFloat;
return result;
case VariantType.Double:
result.asBoolean = value1.asDouble <= value2.asDouble;
return result;
case VariantType.String:
result.asBoolean = value1.asString <= value2.asString;
return result;
case VariantType.TimeSpan:
result.asBoolean = value1.asTimeSpan <= value2.asTimeSpan;
return result;
case VariantType.DateTime:
result.asBoolean = value1.asDateTime.getTime() <= value2.asDateTime.getTime();
return result;
}
throw new Error("Operation '<=' is not supported for type " + this.typeToString(value1.type));
}
/**
* Performs IN operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public in(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
// Processes null arrays.
if (value1.asObject == null) {
result.asBoolean = false;
return result;
}
if (value1.type == VariantType.Array) {
const array = value1.asArray;
for (const element of array) {
const eq = this.equal(value2, element);
if (eq.type == VariantType.Boolean && eq.asBoolean) {
result.asBoolean = true;
return result;
}
}
result.asBoolean = false;
return result;
}
return this.equal(value1, value2);
}
/**
* Performs [] operation for two variants.
* @param value1 The first operand for this operation.
* @param value2 The second operand for this operation.
* @returns A result variant object.
*/
public getElement(value1: Variant, value2: Variant): Variant {
const result = new Variant();
// Processes VariantType.Null values.
if (value1.type == VariantType.Null || value2.type == VariantType.Null) {
return result;
}
value2 = this.convert(value2, VariantType.Integer);
if (value1.type == VariantType.Array) {
return value1.getByIndex(value2.asInteger);
} else if (value1.type == VariantType.String) {
result.asString = value1.asString.charAt(value2.asInteger);
return result;
}
throw new Error("Operation '[]' is not supported for type " + this.typeToString(value1.type));
}
}