diff options
Diffstat (limited to 'sdnr/wt/odlux/apps/configurationApp/src/yang')
-rw-r--r-- | sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts | 388 |
1 files changed, 298 insertions, 90 deletions
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts index c7ab5e4a3..49c2b9be7 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -1,6 +1,24 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + import { Token, Statement, Module, Identity } from "../models/yang"; -import { ViewSpecification, ViewElement, isViewElementObjectOrList, ViewElementBase, isViewElementReference } from "../models/uiModels"; +import { ViewSpecification, ViewElement, isViewElementObjectOrList, ViewElementBase, isViewElementReference, ViewElementChoise, ViewElementBinary, ViewElementString, isViewElementString, isViewElementNumber, ViewElementNumber, Expression, YangRange, ViewElementUnion } from "../models/uiModels"; import { yangService } from "../services/yangService"; export const splitVPath = (vPath: string, vPathParser: RegExp): RegExpMatchArray[] => { @@ -238,6 +256,8 @@ class YangLexer { export class YangParser { private _groupingsToResolve: (() => void)[] = []; private _identityToResolve: (() => void)[] = []; + private _unionsToResolve: (() => void)[] = []; + private _modules: { [name: string]: Module } = {}; private _views: ViewSpecification[] = [{ @@ -248,7 +268,9 @@ export class YangParser { parentView: "0", title: "root", elements: {}, - }]; + }]; + + public static ResolveStack = Symbol("ResolveStack"); constructor() { @@ -311,12 +333,12 @@ export class YangParser { const revisions = this.extractNodes(rootStatement, "revision"); module.revisions = { ...module.revisions, - ...revisions.reduce<{ [version: string]: { }}>((acc, version) => { + ...revisions.reduce<{ [version: string]: {} }>((acc, version) => { if (!version.arg) { throw new Error(`Module [${module.name}] has a version w/o version number.`); } const description = this.extractValue(version, "description"); - const reference = this.extractValue(version,"reference"); + const reference = this.extractValue(version, "reference"); acc[version.arg] = { description, reference, @@ -345,10 +367,10 @@ export class YangParser { const imports = this.extractNodes(rootStatement, "import"); module.imports = { ...module.imports, - ...imports.reduce < { [key: string]: string }>((acc, imp) => { + ...imports.reduce<{ [key: string]: string }>((acc, imp) => { const prefix = imp.sub && imp.sub.filter(s => s.key === "prefix"); if (!imp.arg) { - throw new Error(`Module [${module.name}] has an import with neither name nor prefix.`); + throw new Error(`Module [${module.name}] has an import with neither name nor prefix.`); } acc[prefix && prefix.length === 1 && prefix[0].arg || imp.arg] = imp.arg; return acc; @@ -389,8 +411,15 @@ export class YangParser { } public postProcess() { - // process all groupings + // execute all post processes like resolving in propper order + this._unionsToResolve.forEach(cb => { + try { cb(); } catch (error) { + console.warn(error.message); + } + }); + + // process all groupings this._groupingsToResolve.forEach(cb => { try { cb(); } catch (error) { console.warn(`Error resolving: [${error.message}]`); @@ -418,7 +447,7 @@ export class YangParser { }); // process Identities - const traverseIdentity = (identities : Identity[]) => { + const traverseIdentity = (identities: Identity[]) => { const result: Identity[] = []; for (let identity of identities) { if (identity.children && identity.children.length > 0) { @@ -438,7 +467,7 @@ export class YangParser { const identity = module.identities[idKey]; if (identity.base != null) { const base = this.resolveIdentity(identity.base, module); - base.children?.push(identity); + base.children ?.push(identity); } else { baseIdentites.push(identity); } @@ -455,7 +484,6 @@ export class YangParser { }); }; - private _nextId = 1; private get nextId() { return this._nextId++; @@ -478,7 +506,7 @@ export class YangParser { private extractTypeDefinitions(statement: Statement, module: Module, currentPath: string): void { const typedefs = this.extractNodes(statement, "typedef"); typedefs && typedefs.forEach(def => { - if (! def.arg) { + if (!def.arg) { throw new Error(`Module: [${module.name}]. Found typefed without name.`); } module.typedefs[def.arg] = this.getViewElement(def, module, 0, currentPath, false); @@ -562,13 +590,14 @@ export class YangParser { // extract conditions const ifFeature = this.extractValue(statement, "if-feature"); const whenCondition = this.extractValue(statement, "when"); + if (whenCondition) console.warn("Found in [" + module.name + "]" + currentPath + " when: " + whenCondition); // extract all container const container = this.extractNodes(statement, "container"); if (container && container.length > 0) { subViews.push(...container.reduce<ViewSpecification[]>((acc, cur) => { if (!cur.arg) { - throw new Error(`Module: [${module.name}]. Found container without name.`); + throw new Error(`Module: [${module.name}]${currentPath}. Found container without name.`); } const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); elements.push({ @@ -589,11 +618,11 @@ export class YangParser { if (lists && lists.length > 0) { subViews.push(...lists.reduce<ViewSpecification[]>((acc, cur) => { if (!cur.arg) { - throw new Error(`Module: [${module.name}]. Found list without name.`); + throw new Error(`Module: [${module.name}]${currentPath}. Found list without name.`); } const key = this.extractValue(cur, "key") || undefined; if (config && !key) { - throw new Error(`Module: [${module.name}]. Found configurable list without key.`); + throw new Error(`Module: [${module.name}]${currentPath}. Found configurable list without key.`); } const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); elements.push({ @@ -635,10 +664,70 @@ export class YangParser { const choiceStms = this.extractNodes(statement, "choice"); if (choiceStms && choiceStms.length > 0) { - for (let i = 0; i < choiceStms.length; ++i) { - const cases = this.extractNodes(choiceStms[i], "case"); - console.warn(`Choice found ${choiceStms[i].arg}::${cases.map(c => c.arg).join(";")}`, choiceStms[i]); - } + elements.push(...choiceStms.reduce<ViewElementChoise[]>((accChoise, curChoise) => { + if (!curChoise.arg) { + throw new Error(`Module: [${module.name}]${currentPath}. Found choise without name.`); + } + // extract all cases like containers + const cases: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[] = []; + const caseStms = this.extractNodes(curChoise, "case"); + if (caseStms && caseStms.length > 0) { + cases.push(...caseStms.reduce((accCase, curCase) => { + if (!curCase.arg) { + throw new Error(`Module: [${module.name}]${currentPath}/${curChoise.arg}. Found case without name.`); + } + const description = this.extractValue(curCase, "description") || undefined; + const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, module, `${currentPath}/${module.name}:${curChoise.arg}`); + subViews.push(caseView, ...caseSubViews); + + const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = { + id: parentId === 0 ? `${module.name}:${curCase.arg}` : curCase.arg, + label: curCase.arg, + description: description, + elements: caseView.elements + }; + accCase.push(caseDef); + return accCase; + }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[])); + } + + // extract all simple cases (one case per leaf, container, etc.) + const [choiseView, choiseSubViews] = this.extractSubViews(curChoise, parentId, module, `${currentPath}/${module.name}:${curChoise.arg}`); + subViews.push(choiseView, ...choiseSubViews); + cases.push(...Object.keys(choiseView.elements).reduce((accElm, curElm) => { + const elm = choiseView.elements[curElm]; + const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = { + id: elm.id, + label: elm.label, + description: elm.description, + elements: { [elm.id]: elm } + }; + accElm.push(caseDef); + return accElm; + }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[])); + + const description = this.extractValue(curChoise, "description") || undefined; + const configValue = this.extractValue(curChoise, "config"); + const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + + const mandatory = this.extractValue(curChoise, "mandatory") === "true" || false; + + const element: ViewElementChoise = { + uiType: "choise", + id: parentId === 0 ? `${module.name}:${curChoise.arg}` : curChoise.arg, + label: curChoise.arg, + config: config, + mandatory: mandatory, + description: description, + cases: cases.reduce((acc, cur) => { + acc[cur.id] = cur; + return acc; + }, {} as { [name: string]: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } }) + }; + + accChoise.push(element); + return accChoise; + }, [])); } const rpcs = this.extractNodes(statement, "rpc"); @@ -707,17 +796,74 @@ export class YangParser { return [viewSpec, subViews]; } + // https://tools.ietf.org/html/rfc7950#section-9.3.4 + private static decimalRange = [ + { min: -9223372036854775808, max: 9223372036854775807 }, + { min: -922337203685477580.8, max: 922337203685477580.7 }, + { min: -92233720368547758.08, max: 92233720368547758.07 }, + { min: -9223372036854775.808, max: 9223372036854775.807 }, + { min: -922337203685477.5808, max: 922337203685477.5807 }, + { min: -92233720368547.75808, max: 92233720368547.75807 }, + { min: -9223372036854.775808, max: 9223372036854.775807 }, + { min: -922337203685.4775808, max: 922337203685.4775807 }, + { min: -92233720368.54775808, max: 92233720368.54775807 }, + { min: -9223372036.854775808, max: 9223372036.854775807 }, + { min: -922337203.6854775808, max: 922337203.6854775807 }, + { min: -92233720.36854775808, max: 92233720.36854775807 }, + { min: -9223372.036854775808, max: 9223372.036854775807 }, + { min: -922337.2036854775808, max: 922337.2036854775807 }, + { min: -92233.72036854775808, max: 92233.72036854775807 }, + { min: -9223.372036854775808, max: 9223.372036854775807 }, + { min: -922.3372036854775808, max: 922.3372036854775807 }, + { min: -92.23372036854775808, max: 92.23372036854775807 }, + { min: -9.223372036854775808, max: 9.223372036854775807 }, + ]; + /** Extracts the UI View from the type in the cur statement. */ private getViewElement(cur: Statement, module: Module, parentId: number, currentPath: string, isList: boolean): ViewElement { const type = this.extractValue(cur, "type"); const defaultVal = this.extractValue(cur, "default") || undefined; const description = this.extractValue(cur, "description") || undefined; - const rangeMatch = this.extractValue(cur, "range", /^(\d+)\.\.(\d+)/) || undefined; const configValue = this.extractValue(cur, "config"); const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + const extractRange = (min: number, max: number, property: string = "range"): { expression: Expression<YangRange> | undefined, min: number, max: number } => { + const ranges = this.extractValue(this.extractNodes(cur, "type")[0]!, property) || undefined; + const range = ranges ?.replace(/min/i, String(min)).replace(/max/i, String(max)).split("|").map(r => { + const [minStr, maxStr] = r.split('..'); + const minValue = Number(minStr); + const maxValue = Number(maxStr); + + if (minValue > min) min = minValue; + if (maxValue < max) max = maxValue; + + return { + min: minValue, + max: maxValue + }; + }); + return { + min: min, + max: max, + expression: range && range.length === 1 + ? range[0] + : range && range.length > 1 + ? { operation: "OR", arguments: range } + : undefined + } + }; + + const extractPattern = (): Expression<RegExp> | undefined => { + const pattern = this.extractNodes(this.extractNodes(cur, "type")[0]!, "pattern").map(p => p.arg!).filter(p => !!p).map(p => `^${p}$`); + return pattern && pattern.length == 1 + ? new RegExp(pattern[0]) + : pattern && pattern.length > 1 + ? { operation: "AND", arguments: pattern.map(p => new RegExp(p)) } + : undefined; + } + const mandatory = this.extractValue(cur, "mandatory") === "true" || false; if (!cur.arg) { @@ -729,7 +875,7 @@ export class YangParser { } const element: ViewElementBase = { - id: parentId === 0 ? `${module.name}:${cur.arg}`: cur.arg, + id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg, label: cur.arg, config: config, mandatory: mandatory, @@ -739,10 +885,12 @@ export class YangParser { }; if (type === "string") { + const length = extractRange(0, +18446744073709551615, "length"); return ({ ...element, uiType: "string", - pattern: this.extractNodes(this.extractNodes(cur, "type")[0]!, "pattern").map(p => p.arg!).filter(p => !!p), + length: length.expression, + pattern: extractPattern(), }); } else if (type === "boolean") { return ({ @@ -750,106 +898,109 @@ export class YangParser { uiType: "boolean" }); } else if (type === "uint8") { + const range = extractRange(0, +255); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +255, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "uint16") { + const range = extractRange(0, +65535); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +65535, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "uint32") { + const range = extractRange(0, +4294967295); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +4294967295, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "uint64") { + const range = extractRange(0, +18446744073709551615); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int8") { + const range = extractRange(-128, +127); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : -128, - max: rangeMatch ? Number(rangeMatch[1]) : +127, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int16") { + const range = extractRange(-32768, +32767); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : -32768, - max: rangeMatch ? Number(rangeMatch[1]) : +32767, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int32") { + const range = extractRange(-2147483648, +2147483647); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : -2147483648, - max: rangeMatch ? Number(rangeMatch[1]) : +2147483647, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, }); } else if (type === "int64") { + const range = extractRange(-9223372036854775808, +9223372036854775807); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, - }); - } else if (type === "decimal16") { - return ({ - ...element, - uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, - fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 - }); - } else if (type === "decimal32") { - return ({ - ...element, - uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, - fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 }); } else if (type === "decimal64") { + // decimalRange + const fDigits = Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1; + if (fDigits === -1) { + throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found decimal64 with invalid fraction-digits.`); + } + const range = extractRange(YangParser.decimalRange[fDigits].min, YangParser.decimalRange[fDigits].max); return ({ ...element, uiType: "number", - min: rangeMatch ? Number(rangeMatch[0]) : 0, - max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + fDigits: fDigits, + range: range.expression, + min: range.min, + max: range.max, units: this.extractValue(cur, "units") || undefined, format: this.extractValue(cur, "format") || undefined, - fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 }); } else if (type === "enumeration") { const typeNode = this.extractNodes(cur, "type")[0]!; @@ -881,7 +1032,7 @@ export class YangParser { } const refPath = this.resolveReferencePath(vPath, module); const resolve = this.resolveReference.bind(this); - const res : ViewElement = { + const res: ViewElement = { ...element, uiType: "reference", referencePath: refPath, @@ -912,7 +1063,7 @@ export class YangParser { options: [] }; this._identityToResolve.push(() => { - const identity : Identity = this.resolveIdentity(base, module); + const identity: Identity = this.resolveIdentity(base, module); if (!identity) { throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Could not resolve identity [${base}].`); } @@ -925,7 +1076,7 @@ export class YangParser { description: val.description })); }); - return res ; + return res; } else if (type === "empty") { // todo: ❗ handle empty ⚡ /* 9.11. The empty Built-In Type @@ -939,18 +1090,41 @@ export class YangParser { } else if (type === "union") { // todo: ❗ handle union ⚡ /* 9.12. The union Built-In Type */ - console.warn(`found type: union in [${module.name}][${currentPath}][${element.label}]`); - return { + const typeNode = this.extractNodes(cur, "type")[0]!; + const typeNodes = this.extractNodes(typeNode, "type"); + + const resultingElement = { ...element, - uiType: "string", + uiType: "union", + elements: [] + } as ViewElementUnion; + + const resolveUnion = () => { + resultingElement.elements.push(...typeNodes.map(node => { + const stm: Statement = { + ...cur, + sub: [ + ...(cur.sub ?.filter(s => s.key !== "type") || []), + node + ] + }; + return { + ...this.getViewElement(stm, module, parentId, currentPath, isList), + id: node.arg! + }; + })); }; + + this._unionsToResolve.push(resolveUnion); + + return resultingElement; } else if (type === "bits") { const typeNode = this.extractNodes(cur, "type")[0]!; const bitNodes = this.extractNodes(typeNode, "bit"); return { ...element, uiType: "bits", - flags: bitNodes.reduce<{[name: string]: number | undefined; }>((acc, bitNode) => { + flags: bitNodes.reduce<{ [name: string]: number | undefined; }>((acc, bitNode) => { if (!bitNode.arg) { throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found bit without name.`); } @@ -961,25 +1135,60 @@ export class YangParser { }, {}) }; } else if (type === "binary") { - const typeNode = this.extractNodes(cur, "type")[0]!; - const length = Number(this.extractValue(typeNode, "length")); return { ...element, uiType: "binary", - length: length === length ? length : undefined + length: extractRange(0, +18446744073709551615, "length"), }; } else { // not a build in type, have to resolve type - const typeRef = this.resolveType(type, module); + let typeRef = this.resolveType(type, module); if (typeRef == null) console.error(new Error(`Could not resolve type ${type} in [${module.name}][${currentPath}].`)); + + if (isViewElementString(typeRef)) { + typeRef = this.resolveStringType(typeRef, extractPattern(), extractRange(0, +18446744073709551615)); + + } else if (isViewElementNumber(typeRef)) { + typeRef = this.resolveNumberType(typeRef, extractRange(typeRef.min, typeRef.max)); + } + return ({ ...typeRef, ...element, - description: description + description: description, }) as ViewElement; } } + private resolveStringType(parentElement: ViewElementString, pattern: Expression<RegExp> | undefined, length: { expression: Expression<YangRange> | undefined, min: number, max: number }) { + return { + ...parentElement, + pattern: pattern != null && parentElement.pattern + ? { operation: "AND", arguments: [pattern, parentElement.pattern] } + : parentElement.pattern + ? parentElement.pattern + : pattern, + length: length.expression != null && parentElement.length + ? { operation: "AND", arguments: [length.expression, parentElement.length] } + : parentElement.length + ? parentElement.length + : length ?.expression, + } as ViewElementString; + } + + private resolveNumberType(parentElement: ViewElementNumber, range: { expression: Expression<YangRange> | undefined, min: number, max: number }) { + return { + ...parentElement, + range: range.expression != null && parentElement.range + ? { operation: "AND", arguments: [range.expression, parentElement.range] } + : parentElement.range + ? parentElement.range + : range, + min: range.min, + max: range.max, + } as ViewElementNumber; + } + private resolveReferencePath(vPath: string, module: Module) { const vPathParser = /(?:(?:([^\/\:]+):)?([^\/]+))/g // 1 = opt: namespace / 2 = property return vPath.replace(vPathParser, (_, ns, property) => { @@ -988,10 +1197,9 @@ export class YangParser { }); } - private resolveReference(vPath: string, currentPath: string) { const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath - let element : ViewElement | null = null; + let element: ViewElement | null = null; let moduleName = ""; const vPathParts = splitVPath(vPath, vPathParser).map(p => ({ ns: p[1], property: p[2], ind: p[3] })); @@ -999,7 +1207,7 @@ export class YangParser { ? splitVPath(currentPath, vPathParser).map(p => ({ ns: p[1], property: p[2], ind: p[3] })) : []; - for (let i = 0; i < vPathParts.length; ++i){ + for (let i = 0; i < vPathParts.length; ++i) { const vPathPart = vPathParts[i]; if (vPathPart.property === "..") { resultPathParts.pop(); @@ -1009,26 +1217,26 @@ export class YangParser { } // resolve element by path - for (let j = 0; j < resultPathParts.length;++j){ + for (let j = 0; j < resultPathParts.length; ++j) { const pathPart = resultPathParts[j]; - if (j===0) { + if (j === 0) { + moduleName = pathPart.ns; + const rootModule = this._modules[moduleName]; + if (!rootModule) throw new Error("Could not resolve module [" + moduleName + "].\r\n" + vPath); + element = rootModule.elements[`${pathPart.ns}:${pathPart.property}`]; + } else if (element && isViewElementObjectOrList(element)) { + const view: ViewSpecification = this._views[+element.viewId]; + if (moduleName !== pathPart.ns) { moduleName = pathPart.ns; - const rootModule = this._modules[moduleName]; - if (!rootModule) throw new Error("Could not resolve module [" + moduleName +"].\r\n" + vPath); - element = rootModule.elements[`${pathPart.ns}:${pathPart.property}`]; - } else if (element && isViewElementObjectOrList(element)) { - const view: ViewSpecification = this._views[+element.viewId]; - if (moduleName !== pathPart.ns) { - moduleName = pathPart.ns; - element = view.elements[`${moduleName}:${pathPart.property}`]; - } else { - element = view.elements[pathPart.property] || view.elements[`${moduleName}:${pathPart.property}`]; - } + element = view.elements[`${moduleName}:${pathPart.property}`]; } else { - throw new Error("Could not resolve reference.\r\n" + vPath); + element = view.elements[pathPart.property] || view.elements[`${moduleName}:${pathPart.property}`]; } - if (!element) throw new Error("Could not resolve path [" + pathPart.property + "] in ["+ currentPath +"] \r\n" + vPath); + } else { + throw new Error("Could not resolve reference.\r\n" + vPath); } + if (!element) throw new Error("Could not resolve path [" + pathPart.property + "] in [" + currentPath + "] \r\n" + vPath); + } return element; } |