hyperformula
Version:
HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas
223 lines (221 loc) • 9.29 kB
JavaScript
;
exports.__esModule = true;
exports.columnAddressFromString = exports.cellAddressFromString = void 0;
exports.columnIndexToLabel = columnIndexToLabel;
exports.rowAddressFromString = void 0;
exports.sheetIndexToString = sheetIndexToString;
exports.simpleCellRangeToString = exports.simpleCellRangeFromString = exports.simpleCellAddressToString = exports.simpleCellAddressFromString = void 0;
var _AbsoluteCellRange = require("../AbsoluteCellRange");
var _Cell = require("../Cell");
var _CellAddress = require("./CellAddress");
var _ColumnAddress = require("./ColumnAddress");
var _parserConsts = require("./parser-consts");
var _RowAddress = require("./RowAddress");
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/
const addressRegex = new RegExp(`^(${_parserConsts.SHEET_NAME_PATTERN})?(\\${_parserConsts.ABSOLUTE_OPERATOR}?)([A-Za-z]+)(\\${_parserConsts.ABSOLUTE_OPERATOR}?)([0-9]+)$`);
const columnRegex = new RegExp(`^(${_parserConsts.SHEET_NAME_PATTERN})?(\\${_parserConsts.ABSOLUTE_OPERATOR}?)([A-Za-z]+)$`);
const rowRegex = new RegExp(`^(${_parserConsts.SHEET_NAME_PATTERN})?(\\${_parserConsts.ABSOLUTE_OPERATOR}?)([0-9]+)$`);
const simpleSheetNameRegex = new RegExp(`^${_parserConsts.UNQUOTED_SHEET_NAME_PATTERN}$`);
/**
* Computes R0C0 representation of cell address based on it's string representation and base address.
*
* @param {string} stringAddress - string representation of cell address, e.g., 'C64'
* @param {SimpleCellAddress} baseAddress - base address for R0C0 conversion
* @param {ResolveSheetReferenceFn} resolveSheetReference - mapping function needed to change name of a sheet to index
* @returns {Maybe<CellAddress>} object representation of address or `undefined` if the sheet cannot be resolved
*/
const cellAddressFromString = (stringAddress, baseAddress, resolveSheetReference) => {
const result = addressRegex.exec(stringAddress);
if (!result) {
return undefined;
}
const col = columnLabelToIndex(result[6]);
const row = Number(result[8]) - 1;
const sheetName = extractSheetName(result);
const sheet = sheetNameToId(sheetName, resolveSheetReference);
if (sheet === null) {
return undefined;
}
if (result[5] === _parserConsts.ABSOLUTE_OPERATOR && result[7] === _parserConsts.ABSOLUTE_OPERATOR) {
return _CellAddress.CellAddress.absolute(col, row, sheet);
} else if (result[5] === _parserConsts.ABSOLUTE_OPERATOR) {
return _CellAddress.CellAddress.absoluteCol(col, row - baseAddress.row, sheet);
} else if (result[7] === _parserConsts.ABSOLUTE_OPERATOR) {
return _CellAddress.CellAddress.absoluteRow(col - baseAddress.col, row, sheet);
} else {
return _CellAddress.CellAddress.relative(col - baseAddress.col, row - baseAddress.row, sheet);
}
};
exports.cellAddressFromString = cellAddressFromString;
const columnAddressFromString = (stringAddress, baseAddress, resolveSheetReference) => {
const result = columnRegex.exec(stringAddress);
if (!result) {
return undefined;
}
const col = columnLabelToIndex(result[6]);
const sheetName = extractSheetName(result);
const sheet = sheetNameToId(sheetName, resolveSheetReference);
if (sheet === null) {
return undefined;
}
if (result[5] === _parserConsts.ABSOLUTE_OPERATOR) {
return _ColumnAddress.ColumnAddress.absolute(col, sheet);
} else {
return _ColumnAddress.ColumnAddress.relative(col - baseAddress.col, sheet);
}
};
exports.columnAddressFromString = columnAddressFromString;
const rowAddressFromString = (stringAddress, baseAddress, resolveSheetReference) => {
const result = rowRegex.exec(stringAddress);
if (!result) {
return undefined;
}
const row = Number(result[6]) - 1;
const sheetName = extractSheetName(result);
const sheet = sheetNameToId(sheetName, resolveSheetReference);
if (sheet === null) {
return undefined;
}
if (result[5] === _parserConsts.ABSOLUTE_OPERATOR) {
return _RowAddress.RowAddress.absolute(row, sheet);
} else {
return _RowAddress.RowAddress.relative(row - baseAddress.row, sheet);
}
};
/**
* Computes simple (absolute) address of a cell address based on its string representation.
* - If sheet name is present in the string representation but is not present in sheet mapping, returns `undefined`.
* - If sheet name is not present in the string representation, returns {@param contextSheetId} as sheet number.
*
* @param {ResolveSheetReferenceFn} resolveSheetReference - mapping function needed to change name of a sheet to index
* @param {string} stringAddress - string representation of cell address, e.g., 'C64'
* @param {number} contextSheetId - sheet in context of which we should parse the address
* @returns {Maybe<SimpleCellAddress>} absolute representation of address, e.g., { sheet: 0, col: 1, row: 1 }
*/
exports.rowAddressFromString = rowAddressFromString;
const simpleCellAddressFromString = (resolveSheetReference, stringAddress, contextSheetId) => {
const regExpExecArray = addressRegex.exec(stringAddress);
if (!regExpExecArray) {
return undefined;
}
const col = columnLabelToIndex(regExpExecArray[6]);
const row = Number(regExpExecArray[8]) - 1;
const sheetName = extractSheetName(regExpExecArray);
const sheet = sheetNameToId(sheetName, resolveSheetReference);
if (sheet === null) {
return undefined;
}
const effectiveSheet = sheet === undefined ? contextSheetId : sheet;
return (0, _Cell.simpleCellAddress)(effectiveSheet, col, row);
};
exports.simpleCellAddressFromString = simpleCellAddressFromString;
const simpleCellRangeFromString = (resolveSheetReference, stringAddress, contextSheetId) => {
const split = stringAddress.split(_parserConsts.RANGE_OPERATOR);
if (split.length !== 2) {
return undefined;
}
const [startString, endString] = split;
const start = simpleCellAddressFromString(resolveSheetReference, startString, contextSheetId);
if (start === undefined) {
return undefined;
}
const end = simpleCellAddressFromString(resolveSheetReference, endString, start.sheet);
if (end === undefined) {
return undefined;
}
if (start.sheet !== end.sheet) {
return undefined;
}
return (0, _AbsoluteCellRange.simpleCellRange)(start, end);
};
/**
* Returns string representation of absolute address
* If sheet index is not present in sheet mapping, returns undefined
*/
exports.simpleCellRangeFromString = simpleCellRangeFromString;
const simpleCellAddressToString = (sheetIndexMapping, address, sheetIndex) => {
const column = columnIndexToLabel(address.col);
const sheetName = sheetIndexToString(address.sheet, sheetIndexMapping);
if (sheetName === undefined) {
return undefined;
}
if (sheetIndex !== address.sheet) {
return `${sheetName}!${column}${address.row + 1}`;
} else {
return `${column}${address.row + 1}`;
}
};
exports.simpleCellAddressToString = simpleCellAddressToString;
const simpleCellRangeToString = (sheetIndexMapping, address, sheetIndex) => {
const startString = simpleCellAddressToString(sheetIndexMapping, address.start, sheetIndex);
const endString = simpleCellAddressToString(sheetIndexMapping, address.end, address.start.sheet);
if (startString === undefined || endString === undefined) {
return undefined;
} else {
return `${startString}${_parserConsts.RANGE_OPERATOR}${endString}`;
}
};
/**
* Convert column label to index
*
* @param columnStringRepresentation - column label (e.g., 'AAB')
* @returns column index
*/
exports.simpleCellRangeToString = simpleCellRangeToString;
function columnLabelToIndex(columnStringRepresentation) {
if (columnStringRepresentation.length === 1) {
return columnStringRepresentation.toUpperCase().charCodeAt(0) - 65;
} else {
return columnStringRepresentation.split('').reduce((currentColumn, nextLetter) => {
return currentColumn * 26 + (nextLetter.toUpperCase().charCodeAt(0) - 64);
}, 0) - 1;
}
}
/**
* Converts column index to label
*
* @param column - address to convert
* @returns string representation, e.g., 'AAB'
*/
function columnIndexToLabel(column) {
let result = '';
while (column >= 0) {
result = String.fromCharCode(column % 26 + 97) + result;
column = Math.floor(column / 26) - 1;
}
return result.toUpperCase();
}
function sheetIndexToString(sheetId, sheetMappingFn) {
let sheetName = sheetMappingFn(sheetId);
if (sheetName === undefined) {
return undefined;
}
if (simpleSheetNameRegex.test(sheetName)) {
return sheetName;
} else {
sheetName = sheetName.replace(/'/g, "''");
return `'${sheetName}'`;
}
}
function extractSheetName(regexResult) {
var _a;
const maybeSheetName = (_a = regexResult[3]) !== null && _a !== void 0 ? _a : regexResult[2];
return maybeSheetName ? maybeSheetName.replace(/''/g, "'") : null;
}
/**
* Resolves sheet name to sheet id.
*
* @param sheetName - extracted sheet name or null when not provided.
* @param resolveSheetReference - mapping function resolving sheet name to id.
* @returns sheet id, undefined when sheet name absent, null when resolution fails.
*/
function sheetNameToId(sheetName, resolveSheetReference) {
if (!sheetName) {
return undefined;
}
const sheetId = resolveSheetReference(sheetName);
return sheetId === undefined ? null : sheetId;
}