@abaplint/runtime
Version:
Transpiler - Runtime
554 lines • 18.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Table = exports.HashedTable = exports.TableFactory = exports.LoopController = exports.TableKeyType = exports.TableAccessType = void 0;
const integer_1 = require("./integer");
const abap_object_1 = require("./abap_object");
const string_1 = require("./string");
const structure_1 = require("./structure");
const field_symbol_1 = require("./field_symbol");
const data_reference_1 = require("./data_reference");
const insert_internal_1 = require("../statements/insert_internal");
const sort_1 = require("../statements/sort");
const character_1 = require("./character");
const hex_1 = require("./hex");
const hex_uint8_1 = require("./hex_uint8");
var TableAccessType;
(function (TableAccessType) {
TableAccessType["standard"] = "STANDARD";
TableAccessType["sorted"] = "SORTED";
TableAccessType["hashed"] = "HASHED";
TableAccessType["index"] = "INDEX";
TableAccessType["any"] = "ANY";
})(TableAccessType || (exports.TableAccessType = TableAccessType = {}));
var TableKeyType;
(function (TableKeyType) {
TableKeyType["default"] = "DEFAULT";
TableKeyType["user"] = "USER";
TableKeyType["empty"] = "EMPTY";
})(TableKeyType || (exports.TableKeyType = TableKeyType = {}));
class LoopController {
index;
loopTo;
array;
constructor(from, loopTo, array) {
this.index = from;
this.loopTo = loopTo;
this.array = array;
}
}
exports.LoopController = LoopController;
class TableFactory {
static construct(rowType, options, qualifiedName) {
if (options === undefined) {
options = {
primaryKey: {
name: "primary_key",
type: TableAccessType.standard,
keyFields: [],
isUnique: false,
},
keyType: TableKeyType.default,
withHeader: false,
};
}
if (options.primaryKey?.type === TableAccessType.hashed) {
return new HashedTable(rowType, options, qualifiedName);
}
else {
return new Table(rowType, options, qualifiedName);
}
}
}
exports.TableFactory = TableFactory;
/*
export class SortedTable {
// todo
}
*/
class HashedTable {
value;
header;
rowType;
loops;
options;
qualifiedName;
isStructured;
secondaryIndexes;
constructor(rowType, options, qualifiedName) {
this.value = {};
this.secondaryIndexes = {};
this.loops = new Set();
this.rowType = rowType;
this.options = options;
this.isStructured = rowType instanceof structure_1.Structure;
if (options?.withHeader === true) {
this.header = this.rowType.clone();
}
this.qualifiedName = qualifiedName?.toUpperCase();
}
clone() {
const copy = new HashedTable(this.rowType, this.options, this.qualifiedName);
for (const hash in this.value) {
copy.value[hash] = this.value[hash].clone();
}
return copy;
}
getArrayLength() {
return Object.keys(this.value).length;
}
getKeyByName(name) {
return this.getOptions()?.secondary?.find(s => s.name.toUpperCase() === name.toUpperCase());
}
getSecondaryIndex(name) {
if (this.secondaryIndexes[name.toUpperCase()]) {
return this.secondaryIndexes[name.toUpperCase()];
}
const secondary = this.getKeyByName(name);
if (secondary === undefined) {
throw `Table, secondary key "${name}" not found`;
}
// note, array() already is a copy, so it can be used,
const copy = this.array();
(0, sort_1.sort)(copy, { by: secondary.keyFields.map(k => { return { component: k.toLowerCase() }; }) });
this.secondaryIndexes[name.toUpperCase()] = copy;
return copy;
}
buildHashFromData(data) {
let hash = "";
for (const k of this.options.primaryKey.keyFields) {
if (k === "TABLE_LINE") {
if (data instanceof structure_1.Structure) {
hash += k + ":" + data.getCharacter(true) + "|";
}
else if (data instanceof abap_object_1.ABAPObject) {
const moo = data.getInternalID();
hash += k + ":" + moo + "|";
}
else {
// @ts-ignore
hash += k + ":" + data.get() + "|";
}
}
else {
// @ts-ignore
let val = data.get()[k.toLowerCase()];
if (val instanceof structure_1.Structure) {
val = val.getCharacter(true);
}
else if (val instanceof abap_object_1.ABAPObject) {
val = val.getInternalID();
}
else {
val = val.get();
}
hash += k + ":" + val + "|";
}
}
return hash;
}
deleteIndex(_index) {
throw new Error("HashedTable, deleteIndex");
}
deleteFrom(row) {
const hash = this.buildHashFromData(row);
delete this.value[hash];
}
buildHashFromSimple(data) {
let hash = "";
const tableRowType = this.getRowType();
const isStructured = tableRowType instanceof structure_1.Structure;
for (const k of this.options.primaryKey.keyFields) {
let val = data[k.toLowerCase()];
if (val === undefined
&& tableRowType.get()[k.toLowerCase()] instanceof structure_1.Structure
&& Object.keys(tableRowType.get()[k.toLowerCase()].get()).length === 1) {
// todo: this might need to be extended and fixed
val = data[Object.keys(tableRowType.get()[k.toLowerCase()].get())[0]];
}
// convert to correct type, eg Chars have specific length, or rounding,
if (k === "TABLE_LINE") {
const row = tableRowType.clone();
row.set(val.get());
val = row.get();
}
else if (isStructured === true) {
const field = tableRowType.get()[k.toLowerCase()];
// if types match, there is no need to clone
if (field instanceof string_1.String && val instanceof string_1.String) {
val = val.get();
}
else if (field instanceof character_1.Character && val instanceof character_1.Character && field.getLength() === val.getLength()) {
val = val.get();
}
else if (field instanceof hex_1.Hex && val instanceof hex_1.Hex && field.getLength() === val.getLength()) {
val = val.get();
}
else if (field instanceof hex_uint8_1.HexUInt8 && val instanceof hex_uint8_1.HexUInt8 && field.getLength() === val.getLength()) {
val = val.get();
}
else if (val instanceof abap_object_1.ABAPObject) {
val = val.getInternalID();
}
else if (val instanceof structure_1.Structure) {
val = val.getCharacter(true);
}
else {
// convert
const row = field.clone();
row.set(val.get());
val = row.get();
}
}
else {
throw new Error("HashedTable, buildHashFromSimple, unexpected type");
}
hash += k + ":" + val + "|";
}
return hash;
}
read(hash) {
return this.value[hash];
}
insert(data) {
const hash = this.buildHashFromData(data);
if (this.value[hash] !== undefined) {
return { value: undefined, subrc: 4 };
}
else {
const val = this.cloneRow(data);
for (const loopController of this.loops.values()) {
loopController.array.push(val);
}
this.secondaryIndexes = {};
this.value[hash] = val;
return { value: val, subrc: 0 };
}
}
appendThis(data) {
this.insert(data);
return this;
}
array() {
// used for LOOP
const ret = [];
for (const hash in this.value) {
ret.push(this.value[hash]);
}
return ret;
}
startLoop(from, to, array) {
const l = new LoopController(from, to, array);
this.loops.add(l);
return l;
}
unregisterLoop(loop) {
this.loops.delete(loop);
}
insertIndex(_item, _index) {
throw new Error("Hash table insert index");
}
append(_item) {
throw new Error("Hash table append");
}
getQualifiedName() {
return this.qualifiedName;
}
getOptions() {
return this.options;
}
getRowType() {
return this.rowType;
}
clear() {
this.value = {};
this.secondaryIndexes = {};
}
set(tab) {
if (tab instanceof field_symbol_1.FieldSymbol) {
if (tab.getPointer() === undefined) {
throw new Error("GETWA_NOT_ASSIGNED");
}
return this.set(tab.getPointer());
}
if (tab === this) {
return this;
}
this.clear();
if (tab instanceof Table || tab instanceof HashedTable) {
for (const a of tab.array()) {
this.insert(a);
}
return this;
}
else {
throw new Error("Method not implemented, set hashed table");
}
}
getHeader() {
if (this.header === undefined) {
throw "table, getHeader";
}
return this.header;
}
///////////////////////////
cloneRow(item) {
// make sure to do conversion if needed
if (typeof item === "number") {
const tmp = this.getRowType().clone();
tmp.set(new integer_1.Integer().set(item));
return tmp;
}
else if (typeof item === "string") {
const tmp = this.getRowType().clone();
tmp.set(new string_1.String().set(item));
return tmp;
// @ts-ignore
}
else if (this.isStructured === true && item.getQualifiedName && this.rowType.getQualifiedName && item.getQualifiedName() !== "" && item.getQualifiedName() === this.rowType.getQualifiedName()) {
// types match, so no need to do conversions, just clone the item
const val = item.clone();
return val;
}
else {
const tmp = this.getRowType().clone();
tmp.set(item);
return tmp;
}
}
}
exports.HashedTable = HashedTable;
class Table {
value;
header;
rowType;
loops;
options;
qualifiedName;
isStructured;
secondaryIndexes;
constructor(rowType, options, qualifiedName) {
this.value = [];
this.secondaryIndexes = {};
this.loops = new Set();
this.rowType = rowType;
this.options = options;
this.isStructured = rowType instanceof structure_1.Structure;
if (options?.withHeader === true) {
this.header = this.rowType.clone();
}
this.qualifiedName = qualifiedName?.toUpperCase();
}
clone() {
const copy = new Table(this.rowType, this.options, this.qualifiedName);
for (const val of this.value) {
// @ts-ignore
copy.value.push(val.clone());
}
return copy;
}
getArrayLength() {
return this.value.length;
}
getKeyByName(name) {
return this.getOptions()?.secondary?.find(s => s.name.toUpperCase() === name.toUpperCase());
}
getSecondaryIndex(name) {
if (this.secondaryIndexes[name.toUpperCase()]) {
return this.secondaryIndexes[name.toUpperCase()];
}
const secondary = this.getKeyByName(name);
if (secondary === undefined) {
throw `Table, secondary key "${name}" not found`;
}
const copy = [...this.value];
(0, sort_1.sort)(copy, { by: secondary.keyFields.map(k => { return { component: k.toLowerCase() }; }), skipSortedCheck: true });
this.secondaryIndexes[name.toUpperCase()] = copy;
return copy;
}
getQualifiedName() {
return this.qualifiedName;
}
getOptions() {
return this.options;
}
startLoop(from, to, array) {
const l = new LoopController(from, to, array);
this.loops.add(l);
return l;
}
getCurrentLoopIndex() {
if (this.loops.size !== 1) {
throw new Error("More than one LOOP");
}
return Array.from(this.loops)[0].index;
}
unregisterLoop(loop) {
this.loops.delete(loop);
}
getRowType() {
return this.rowType;
}
// Modifications to the array must be done inside this class, in order to keep track of LOOP indexes
array() {
return this.value;
}
clear() {
this.value = [];
this.secondaryIndexes = {};
}
set(tab) {
this.secondaryIndexes = {};
if (this.options?.withHeader === true) {
this.header?.set(tab);
}
else {
if (tab instanceof field_symbol_1.FieldSymbol) {
tab = tab.getPointer();
}
if (!(tab instanceof Table)
&& !(tab instanceof HashedTable)) {
throw new Error("Table, set error, " + tab?.constructor.name);
}
if (tab === this) {
return this;
}
this.clear();
// this clones the values, and add sorting if required
(0, insert_internal_1.insertInternal)({ table: this, data: tab, lines: true });
}
return this;
}
getHeader() {
if (this.header === undefined) {
throw new Error("table, getHeader");
}
return this.header;
}
get() {
if (this.header === undefined) {
throw new Error("table, no header line");
}
return this.header.get();
}
insertIndex(item, index, noClone = false) {
this.secondaryIndexes = {};
if (item instanceof field_symbol_1.FieldSymbol) {
const p = item.getPointer();
if (p === undefined) {
throw new Error("insertIndex, fs not assigned");
}
this.insertIndex(p, index);
return p;
}
let val;
if (noClone === false) {
val = this.cloneRow(item);
}
else {
val = item;
}
if (index === 0) {
this.value.unshift(val);
}
else if (index === this.value.length) {
this.value.push(val);
}
else {
this.value.splice(index, 0, val);
}
for (const loopController of this.loops.values()) {
if (index <= loopController.index) {
loopController.index++;
}
}
return val;
}
/** index = javascript indexed */
deleteIndex(index) {
this.secondaryIndexes = {};
if (index > this.value.length) {
return;
}
if (index === this.value.length - 1) {
this.value.pop(); // pop'ing is faster than splice
}
else if (index === 0) {
this.value.shift();
}
else {
this.value.splice(index, 1);
}
for (const l of this.loops.values()) {
if (l.index >= index) {
l.index--;
}
}
}
append(item) {
if (item === undefined) {
throw new Error("APPEND, item is undefined");
}
this.secondaryIndexes = {};
if (item instanceof field_symbol_1.FieldSymbol) {
const p = item.getPointer();
if (p === undefined) {
throw new Error("APPEND, fs not assigned");
}
this.append(p);
return p;
}
else if (item instanceof data_reference_1.DataReference) {
const ref = new data_reference_1.DataReference(item.getType());
ref.assign(item.getPointer());
this.value.push(ref);
return ref;
}
else {
const val = this.cloneRow(item);
this.value.push(val);
return val;
}
}
/* appends and returns this */
appendThis(item) {
this.append(item);
return this;
}
appendInitial() {
this.secondaryIndexes = {};
// note that this will clone the object
this.append(this.rowType);
abap.builtin.sy.get().tabix.set(this.value.length);
return this.value[this.value.length - 1];
}
sort(compareFn) {
this.value.sort(compareFn);
}
///////////////////////////
cloneRow(item) {
// make sure to do conversion if needed
if (typeof item === "number") {
const tmp = this.getRowType().clone();
tmp.set(new integer_1.Integer().set(item));
return tmp;
}
else if (typeof item === "string") {
const tmp = this.getRowType().clone();
tmp.set(new string_1.String().set(item));
return tmp;
// @ts-ignore
}
else if (this.isStructured === true && item.getQualifiedName && this.rowType.getQualifiedName && item.getQualifiedName() !== "" && item.getQualifiedName() === this.rowType.getQualifiedName()) {
// types match, so no need to do conversions, just clone the item
const val = item.clone();
return val;
}
else {
const tmp = this.getRowType().clone();
tmp.set(item);
return tmp;
}
}
}
exports.Table = Table;
//# sourceMappingURL=table.js.map