UNPKG

nope-js-browser

Version:

NoPE Runtime for the Browser. For nodejs please use nope-js-node

283 lines (282 loc) 11.1 kB
/** * @author Martin Karkowski * @email m.karkowski@zema.de * @desc [description] */ import { SPLITCHAR } from "./objectMethods"; export const SEPARATOR = "/"; export const SINGLE_LEVEL_WILDCARD = "+"; export const MULTI_LEVEL_WILDCARD = "#"; /** * Helper to generate a Result. * * @author M.Karkowski * @export * @param {Partial<TPathCompareResult>} [res={}] * @return {*} {TPathCompareResult} */ export function generateResult(res = {}) { let defaultResult = { affected: false, affectedByChild: false, affectedByParent: false, affectedOnSameLevel: false, containsWildcards: false, patternToExtractData: false, patternLengthComparedToPathLength: "=", pathToExtractData: false, }; defaultResult = Object.assign(defaultResult, res); defaultResult.affected = defaultResult.affectedByChild || defaultResult.affectedByParent || defaultResult.affectedOnSameLevel; return Object.assign(defaultResult, res); } /** * Matches the given path, with the pattern and determines, if the path might affect * the given pattern. * * @example path = "a/b/c"; pattern = "a/#"; => totalPath = "a/b/c"; diffPath = "b/c" * @author M.Karkowski * @export * @param {string} pathPattern The pattern to test * @param {string} contentPath The path to use as basis * @return {TPathCompareResult} */ export function comparePatternAndPath(pathPattern, contentPath, options = { matchTopicsWithoutWildcards: false, }) { if (containsWildcards(contentPath)) { throw Error("The Path is invalid. The path should not contain pattern-related chars '#' or '+'."); } if (!patternIsValid(pathPattern)) { throw Error("The Pattern is invalid."); } if (!patternIsValid(contentPath)) { throw Error("The Path is invalid."); } const _containsWildcards = containsWildcards(pathPattern); const patternSegments = pathPattern.split(SEPARATOR); const contentPathSegments = contentPath.split(SEPARATOR); const patternLength = patternSegments.length; const contentPathLength = contentPathSegments.length; // Define the Char for the comparer let patternLengthComparedToPathLength = "="; if (patternLength > contentPathLength) patternLengthComparedToPathLength = ">"; else if (patternLength < contentPathLength) patternLengthComparedToPathLength = "<"; // If both, the pattern and the path are equal => return the result. if (pathPattern === contentPath) { return generateResult({ affectedOnSameLevel: true, pathToExtractData: contentPath, patternLengthComparedToPathLength, }); } // If the Path is not realy defined. if (contentPath === "") { return generateResult({ affectedByParent: true, patternToExtractData: _containsWildcards ? pathPattern : false, pathToExtractData: _containsWildcards ? false : pathPattern, patternLengthComparedToPathLength: ">", containsWildcards: _containsWildcards, }); } if (pathPattern === "") { return generateResult({ affectedByChild: true, pathToExtractData: "", patternLengthComparedToPathLength: "<", }); } if (options.matchTopicsWithoutWildcards) { if (contentPath.startsWith(pathPattern)) { // Path is longer then the Pattern; // => A Change is performed by "Child", if (_containsWildcards) { return generateResult({ affectedByChild: true, pathToExtractData: contentPath, patternLengthComparedToPathLength: patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } else { return generateResult({ affectedByChild: true, pathToExtractData: pathPattern, patternLengthComparedToPathLength: patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } } else if (pathPattern.startsWith(contentPath)) { // Pattern is longer then the path; // => A Change might be initated by // the super element // The PathToExtractData is allways false, if the path is smaller then the // pattern if (_containsWildcards) { return generateResult({ affectedByParent: true, patternToExtractData: pathPattern, patternLengthComparedToPathLength: patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } else { return generateResult({ affectedByParent: true, // No Pattern is used. pathToExtractData: pathPattern, patternLengthComparedToPathLength: patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } } } let partialPath = ""; // Iterate over the Segments. for (let i = 0; i < patternLength; i++) { // Store the current Pattern Segment const currentPattern = patternSegments[i]; // We need to know, if there is SINGLE_LEVEL_WILDCARD or MULTI_LEVEL_WILDCARD // there fore we will extract the Wildlevels. const patternChar = currentPattern[0]; const currentPath = contentPathSegments[i]; if (currentPath === undefined) { // Our Pattern is larger then our contentPath. // So we dont know, whether we will get some // data. Therefore we have to perform a query // later ==> Set The Path / Pattern. if (_containsWildcards) { // But we contain Patterns. // So we are not allowed to build a // diff object. return generateResult({ affectedByParent: true, patternToExtractData: pathPattern, patternLengthComparedToPathLength: patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } else if (patternLengthComparedToPathLength === ">") { // Fixing: it is possible to have a longer return generateResult({ affectedByParent: true, pathToExtractData: pathPattern, patternLengthComparedToPathLength: patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } else { throw Error("Implementation Error! This should not happen."); } } else if (currentPath == currentPattern) { // The Patterns Match // We now store the correct path of our segment. partialPath = partialPath.length > 0 ? `${partialPath}${SEPARATOR}${currentPath}` : currentPath; } else if (patternChar === MULTI_LEVEL_WILDCARD) { // We know, that MULTI_LEVEL_WILDCARDs are only at the end of the // pattern. So it might happen, that: // a) our length of the pattern is the same length as the content path // b) our length of the pattern is smaller then length as the content path // // Our statement before alread tested, that either case a) or b) fits. Otherwise // another ifstatement is valid and we wont enter this statement here. // // We add the segment to testedCorrectPath // testedCorrectPath = testedCorrectPath.length > 0 ? `${testedCorrectPath}${SEPARATOR}${currentPath}` : currentPath; if (patternLengthComparedToPathLength == "=") { // Case a) return generateResult({ affectedOnSameLevel: true, pathToExtractData: contentPath, patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } else if (patternLengthComparedToPathLength == "<") { // Case b) return generateResult({ affectedByChild: true, pathToExtractData: contentPath, patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } else { throw Error("Implementation Error!"); } } else if (patternChar === SINGLE_LEVEL_WILDCARD) { // Store the correct path. partialPath = partialPath.length > 0 ? `${partialPath}${exports.SEPARATOR}${currentPath}` : currentPath; } else if (patternChar !== SINGLE_LEVEL_WILDCARD && currentPattern !== currentPath) { return generateResult({ patternLengthComparedToPathLength: patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } } const diff = contentPath.slice(partialPath.length + 1); return generateResult({ affectedOnSameLevel: diff.length == 0, affectedByChild: diff.length >= 1, pathToExtractData: partialPath, patternLengthComparedToPathLength, containsWildcards: _containsWildcards, }); } /** * Determines, whether the given string contains a single level card or not. * * @author M.Karkowski * @export * @param {string} str String to check * @return {*} {boolean} */ export function containsWildcards(str) { return (str.includes(SINGLE_LEVEL_WILDCARD) || str.includes(MULTI_LEVEL_WILDCARD)); } /** * Function to test if a pattern is valid * * * @author M.Karkowski * @export * @param {string} str * @return {*} {boolean} */ export function patternIsValid(str) { if (str === "") { return true; } const splitted = str.split(SPLITCHAR); const lastIndex = splitted.length - 1; return splitted .map((value, idx) => { if (value) { if (value === MULTI_LEVEL_WILDCARD) { return idx === lastIndex; } return true; } return false; }) .reduce((prev, current) => { return prev && current; }, true); }