"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CdmCorpusDefinition = void 0;
const internal_1 = require("../internal");
const cdmObjectTypeGuards_1 = require("../Utilities/cdmObjectTypeGuards");
const Persistence_1 = require("../Persistence");
const StorageUtils_1 = require("../Utilities/StorageUtils");
const Logger_1 = require("../Utilities/Logging/Logger");
const using_statement_1 = require("using-statement");
class CdmCorpusDefinition {
    constructor() {
        this.TAG = CdmCorpusDefinition.name;
        /**
         * @internal
         */
        this.isCurrentlyResolving = false;
        this.cdmExtension = internal_1.CdmConstants.cdmExtension;
        // let bodyCode = () =>
        {
            // this.rootPath = rootPath;
            this.namespaceFolders = new Map();
            this.symbolDefinitions = new Map();
            this.definitionReferenceSymbols = new Map();
            this.emptyRTS = new Map();
            this.outgoingRelationships = new Map();
            this.incomingRelationships = new Map();
            this.resEntMap = new Map();
            this.documentLibrary = new internal_1.DocumentLibrary(this);
            this.ctx = new internal_1.resolveContext(this);
            this.storage = new internal_1.StorageManager(this);
            this.persistence = new Persistence_1.PersistenceLayer(this);
            // the default for the default is to make entity attributes into foreign key references
            // when they point at one other instance and to ignore the other entities when there are an array of them
            this.defaultResolutionDirectives = new internal_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'referenceOnly']));
        }
        // return p.measure(bodyCode);
    }
    get profiler() {
        return internal_1.p;
    }
    /**
     * @internal
     */
    static nextID() {
        this._nextID++;
        return this._nextID;
    }
    /**
     * @internal
     */
    static mapReferenceType(ofType) {
        // let bodyCode = () =>
        {
            switch (ofType) {
                case internal_1.cdmObjectType.argumentDef:
                case internal_1.cdmObjectType.documentDef:
                case internal_1.cdmObjectType.manifestDef:
                case internal_1.cdmObjectType.import:
                case internal_1.cdmObjectType.parameterDef:
                default:
                    return internal_1.cdmObjectType.error;
                case internal_1.cdmObjectType.attributeGroupRef:
                case internal_1.cdmObjectType.attributeGroupDef:
                    return internal_1.cdmObjectType.attributeGroupRef;
                case internal_1.cdmObjectType.constantEntityDef:
                case internal_1.cdmObjectType.entityDef:
                case internal_1.cdmObjectType.entityRef:
                    return internal_1.cdmObjectType.entityRef;
                case internal_1.cdmObjectType.dataTypeDef:
                case internal_1.cdmObjectType.dataTypeRef:
                    return internal_1.cdmObjectType.dataTypeRef;
                case internal_1.cdmObjectType.purposeDef:
                case internal_1.cdmObjectType.purposeRef:
                    return internal_1.cdmObjectType.purposeRef;
                case internal_1.cdmObjectType.traitDef:
                case internal_1.cdmObjectType.traitRef:
                    return internal_1.cdmObjectType.traitRef;
                case internal_1.cdmObjectType.traitGroupDef:
                case internal_1.cdmObjectType.traitGroupRef:
                    return internal_1.cdmObjectType.traitGroupRef;
                case internal_1.cdmObjectType.entityAttributeDef:
                case internal_1.cdmObjectType.typeAttributeDef:
                case internal_1.cdmObjectType.attributeRef:
                    return internal_1.cdmObjectType.attributeRef;
                case internal_1.cdmObjectType.attributeContextDef:
                case internal_1.cdmObjectType.attributeContextRef:
                    return internal_1.cdmObjectType.attributeContextRef;
            }
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    static createCacheKeyFromObject(definition, kind) {
        return `${definition.ID}-${kind}`;
    }
    /**
     * @internal
     */
    static fetchPriorityDoc(docs, importPriority) {
        // let bodyCode = () =>
        {
            let docBest;
            let indexBest = Number.MAX_SAFE_INTEGER;
            for (const docDefined of docs) {
                // is this one of the imported docs?
                const importInfo = importPriority.get(docDefined);
                if (importInfo && importInfo.priority < indexBest) {
                    indexBest = importInfo.priority;
                    docBest = docDefined;
                    if (indexBest === 0) {
                        break;
                    } // hard to be better than the best
                }
            }
            return docBest;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @deprecated Use fetchObjectAsync instead.
     */
    createRootManifest(corpusPath) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.isPathManifestDocument(corpusPath)) {
                this.rootManifest = yield this.fetchObjectAsync(corpusPath, undefined, false);
                return this.rootManifest;
            }
        });
    }
    /**
     * @internal
     */
    createEmptyResolvedTraitSet(resOpt) {
        // let bodyCode = () =>
        {
            let key = '';
            if (resOpt) {
                if (resOpt.wrtDoc) {
                    key = resOpt.wrtDoc.ID.toString();
                }
                key += '-';
                if (resOpt.directives) {
                    key += resOpt.directives.getTag();
                }
            }
            let rts = this.emptyRTS.get(key);
            if (!rts) {
                rts = new internal_1.ResolvedTraitSet(resOpt);
                this.emptyRTS.set(key, rts);
            }
            return rts;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    docsForSymbol(resOpt, wrtDoc, fromDoc, symbolDef) {
        // let bodyCode = () =>
        {
            const ctx = this.ctx;
            const result = { newSymbol: symbolDef };
            // first decision, is the symbol defined anywhere?
            result.docList = this.symbolDefinitions.get(symbolDef);
            if (!result.docList || result.docList.length === 0) {
                // this can happen when the symbol is disambiguated with a moniker for one of the imports used
                // in this situation, the 'wrt' needs to be ignored,
                // the document where the reference is being made has a map of the 'one best' monikered import to search for each moniker
                const preEnd = symbolDef.indexOf('/');
                if (preEnd === 0) {
                    // absolute refererence
                    internal_1.Logger.error(this.ctx, this.TAG, this.docsForSymbol.name, wrtDoc.atCorpusPath, internal_1.cdmLogCode.ErrUnsupportedRef, symbolDef, ctx.relativePath);
                    return undefined;
                }
                if (preEnd > 0) {
                    const prefix = symbolDef.slice(0, preEnd);
                    result.newSymbol = symbolDef.slice(preEnd + 1);
                    result.docList = this.symbolDefinitions.get(result.newSymbol);
                    let tempMonikerDoc;
                    let usingWrtDoc = false;
                    if (fromDoc && fromDoc.importPriorities && fromDoc.importPriorities.monikerPriorityMap.has(prefix)) {
                        tempMonikerDoc = fromDoc.importPriorities.monikerPriorityMap.get(prefix);
                    }
                    else if (wrtDoc && wrtDoc.importPriorities && wrtDoc.importPriorities.monikerPriorityMap.has(prefix)) {
                        // if that didn't work, then see if the wrtDoc can find the moniker
                        tempMonikerDoc = wrtDoc.importPriorities.monikerPriorityMap.get(prefix);
                        usingWrtDoc = true;
                    }
                    if (tempMonikerDoc) {
                        // if more monikers, keep looking
                        if (result.newSymbol.indexOf('/') >= 0 && (usingWrtDoc || !this.symbolDefinitions.has(result.newSymbol))) {
                            const currDocsResult = this.docsForSymbol(resOpt, wrtDoc, tempMonikerDoc, result.newSymbol);
                            if (!currDocsResult.docList && fromDoc === wrtDoc) {
                                // we are back at the top and we have not found the docs, move the wrtDoc down one level
                                return this.docsForSymbol(resOpt, tempMonikerDoc, tempMonikerDoc, result.newSymbol);
                            }
                            else {
                                return currDocsResult;
                            }
                        }
                        result.docBest = tempMonikerDoc;
                    }
                    else {
                        // moniker not recognized in either doc, fail with grace
                        result.newSymbol = symbolDef;
                        result.docList = undefined;
                    }
                }
            }
            return result;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    resolveSymbolReference(resOpt, fromDoc, symbolDef, expectedType, retry) {
        // given a symbolic name,
        // find the 'highest priority' definition of the object from the point of view of a given document (with respect to, wrtDoc)
        // (meaning given a document and the things it defines and the files it imports and the files they import,
        // where is the 'last' definition found)
        if (!resOpt || !resOpt.wrtDoc || !symbolDef) {
            return undefined;
        } // no way to figure this out
        const wrtDoc = resOpt.wrtDoc;
        if (wrtDoc.needsIndexing && resOpt.importsLoadStrategy === internal_1.importsLoadStrategy.doNotLoad) {
            internal_1.Logger.error(this.ctx, this.TAG, this.resolveSymbolReference.name, wrtDoc.atCorpusPath, internal_1.cdmLogCode.ErrSymbolNotFound, symbolDef, 'because the ImportsLoadStrategy is set to DoNotLoad"');
            return undefined;
        }
        // save the symbol name as it got here
        let initialSymbol = symbolDef;
        // when trying to find a reference, first find the definition that contains it
        // and then look for the reference inside it.
        const isReference = (symbolDef === null || symbolDef === void 0 ? void 0 : symbolDef.endsWith('(ref)')) === true;
        if (isReference) {
            const defIndex = symbolDef.indexOf('/');
            symbolDef = symbolDef.substring(0, defIndex);
        }
        // get the array of documents where the symbol is defined
        const symbolDocsResult = this.docsForSymbol(resOpt, wrtDoc, fromDoc, symbolDef);
        let docBest = symbolDocsResult.docBest;
        symbolDef = symbolDocsResult.newSymbol;
        if (!isReference) {
            initialSymbol = symbolDef;
        }
        const docs = symbolDocsResult.docList;
        if (docs) {
            // add this symbol to the set being collected in resOpt, we will need this when caching
            if (!resOpt.symbolRefSet) {
                resOpt.symbolRefSet = new internal_1.SymbolSet();
            }
            resOpt.symbolRefSet.add(symbolDef);
            // for the given doc, there is a sorted list of imported docs (including the doc itself as item 0).
            // find the lowest number imported document that has a definition for this symbol
            if (!wrtDoc.importPriorities) {
                return undefined; // need to index imports first, should have happened
            }
            const importPriority = wrtDoc.importPriorities.importPriority;
            if (importPriority.size === 0) {
                return undefined;
            }
            if (!docBest) {
                docBest = CdmCorpusDefinition.fetchPriorityDoc(docs, importPriority) || docBest;
            }
        }
        // perhaps we have never heard of this symbol in the imports for this document?
        if (!docBest) {
            return undefined;
        }
        // return the definition found in the best document
        let found = docBest.internalDeclarations.get(symbolDef);
        // in case we are trying to find a reference, the object we found previously is the definition that contains the reference.
        // look inside the definition now.
        if (found && isReference) {
            let foundRef = undefined;
            // try to find the reference
            found.visit('', (obj, objPath) => {
                if (initialSymbol === objPath) {
                    foundRef = obj;
                    return true;
                }
                return false;
            }, undefined);
            found = foundRef;
        }
        if (found === undefined && retry === true) {
            // maybe just locatable from here not defined here.
            // this happens when the symbol is monikered, but the moniker path doesn't lead to the document where the symbol is defined.
            // it leads to the document from where the symbol can be found.
            // Ex.: resolvedFrom/Owner, while resolvedFrom is the Account that imports Owner.
            found = this.resolveSymbolReference(resOpt, docBest, initialSymbol, expectedType, false);
        }
        if (found && expectedType !== internal_1.cdmObjectType.error) {
            found = this.reportErrorStatus(found, symbolDef, expectedType);
        }
        return found;
    }
    /**
     * @internal
     */
    registerDefinitionReferenceSymbols(definition, kind, symbolRefSet) {
        // let bodyCode = () =>
        {
            const key = CdmCorpusDefinition.createCacheKeyFromObject(definition, kind);
            const existingSymbols = this.definitionReferenceSymbols.get(key);
            if (existingSymbols === undefined) {
                // nothing set, just use it
                this.definitionReferenceSymbols.set(key, symbolRefSet);
            }
            else {
                // something there, need to merge
                existingSymbols.merge(symbolRefSet);
            }
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    unRegisterDefinitionReferenceDocuments(definition, kind) {
        // let bodyCode = () =>
        {
            const key = CdmCorpusDefinition.createCacheKeyFromObject(definition, kind);
            this.definitionReferenceSymbols.delete(key);
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    createDefinitionCacheTag(resOpt, definition, kind, extraTags = '', notKnownToHaveParameters = false, pathToDef) {
        // let bodyCode = () =>
        {
            // construct a tag that is unique for a given object in a given context
            // context is:
            //   (1) the wrtDoc has a set of imports and defintions that may change what the object is point at
            //   (2) there are different kinds of things stored per object (resolved traits, atts, etc.)
            //   (3) the directives from the resolve Options might matter
            //   (4) sometimes the caller needs different caches (extraTags) even give 1-3 are the same
            // the hardest part is (1). To do this, see if the object has a set of reference documents registered.
            // if there is nothing registered, there is only one possible way to resolve the object so don't include doc info in the tag.
            // if there IS something registered, then the object could be ambiguous.
            // find the 'index' of each of the ref documents (potential definition of something referenced under this scope)
            // in the wrt document's list of imports. sort the ref docs by their index,
            // the relative ordering of found documents makes a unique context.
            // the hope is that many, many different lists of imported files will result in identical reference sortings, so lots of re-use
            // since this is an expensive operation, actually cache the sorted list associated with this object and wrtDoc
            // easy stuff first
            let thisId;
            const thisPath = (definition.objectType === internal_1.cdmObjectType.projectionDef) ? definition.declaredPath.replace(/\//g, '') : definition.atCorpusPath;
            if (pathToDef && notKnownToHaveParameters) {
                thisId = pathToDef;
            }
            else {
                thisId = definition.ID.toString();
            }
            let tagSuffix = `-${kind}-${thisId}`;
            // Some object types like traits don't change their resolved from depending on the directives.
            // This optimization is only useful when the same corpus is used to resolve objects using different directives.
            let simpleCacheTag = false;
            switch (definition.objectType) {
                case internal_1.cdmObjectType.dataTypeDef:
                case internal_1.cdmObjectType.purposeDef:
                case internal_1.cdmObjectType.traitDef:
                case internal_1.cdmObjectType.traitGroupDef:
                    simpleCacheTag = true;
                    break;
            }
            if (!simpleCacheTag) {
                tagSuffix += `-(${resOpt.directives ? resOpt.directives.getTag() : ''})`;
            }
            // only for attributes
            if (kind === 'rasb') {
                // if MaxDepth was not initialized before, initialize it now
                if (resOpt.depthInfo.maxDepth === undefined) {
                    resOpt.depthInfo.maxDepth = resOpt.maxDepth;
                }
                // add to the cache tag either if we reached maximum depth or how many levels we can go down until reaching the maximum depth
                if (resOpt.depthInfo.currentDepth > resOpt.depthInfo.maxDepth) {
                    tagSuffix += '-overMaxDepth';
                }
                else {
                    const currDepthInfo = resOpt.depthInfo;
                    tagSuffix += `-${currDepthInfo.maxDepth - currDepthInfo.currentDepth}toMaxDepth`;
                }
            }
            if (resOpt.inCircularReference) {
                tagSuffix += '-pk';
            }
            if (extraTags) {
                tagSuffix += `-${extraTags}`;
            }
            // is there a registered set?
            // (for the objectdef, not for a reference) of the many symbols involved in defining this thing(might be none)
            const objDef = definition.fetchObjectDefinition(resOpt);
            let symbolsRef;
            if (objDef) {
                const key = CdmCorpusDefinition.createCacheKeyFromObject(objDef, kind);
                symbolsRef = this.definitionReferenceSymbols.get(key);
            }
            if (symbolsRef === undefined && thisPath !== undefined) {
                // every symbol should depend on at least itself
                const symSetThis = new internal_1.SymbolSet();
                symSetThis.add(thisPath);
                this.registerDefinitionReferenceSymbols(definition, kind, symSetThis);
                symbolsRef = symSetThis;
            }
            if (symbolsRef && symbolsRef.size > 0) {
                // each symbol may have definitions in many documents. use importPriority to figure out which one we want
                const wrtDoc = resOpt.wrtDoc;
                const foundDocIds = new Set();
                if (wrtDoc.importPriorities) {
                    for (const symRef of symbolsRef) {
                        // get the set of docs where defined
                        const docsRes = this.docsForSymbol(resOpt, wrtDoc, definition.inDocument, symRef);
                        // we only add the best doc if there are multiple options
                        if (docsRes !== undefined && docsRes.docList !== undefined && docsRes.docList.length > 1) {
                            const docBest = CdmCorpusDefinition.fetchPriorityDoc(docsRes.docList, wrtDoc.importPriorities.importPriority);
                            if (docBest) {
                                foundDocIds.add(docBest.ID);
                            }
                        }
                    }
                }
                const tagPre = Array.from(foundDocIds)
                    .sort()
                    .join('-');
                return tagPre + tagSuffix;
            }
        }
        // return p.measure(bodyCode);
    }
    MakeRef(ofType, refObj, simpleNameRef) {
        // let bodyCode = () =>
        {
            let oRef;
            if (refObj) {
                if (refObj instanceof internal_1.CdmObjectBase) {
                    if (refObj.objectType === ofType) {
                        // forgive this mistake, return the ref passed in
                        oRef = refObj;
                    }
                    else {
                        oRef = this.MakeObject(ofType, undefined, false);
                        oRef.explicitReference = refObj;
                    }
                }
                else {
                    // refObj is a string or object
                    oRef = this.MakeObject(ofType, refObj, simpleNameRef);
                }
            }
            return oRef;
        }
        // return p.measure(bodyCode);
    }
    MakeObject(ofType, nameOrRef, simmpleNameRef) {
        // let bodyCode = () =>
        {
            let newObj;
            switch (ofType) {
                case internal_1.cdmObjectType.argumentDef:
                    newObj = new internal_1.CdmArgumentDefinition(this.ctx, nameOrRef);
                    newObj.name = nameOrRef;
                    break;
                case internal_1.cdmObjectType.attributeGroupDef:
                    newObj = new internal_1.CdmAttributeGroupDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.attributeGroupRef:
                    newObj = new internal_1.CdmAttributeGroupReference(this.ctx, nameOrRef, simmpleNameRef);
                    break;
                case internal_1.cdmObjectType.constantEntityDef:
                    newObj = new internal_1.CdmConstantEntityDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.dataTypeDef:
                    newObj = new internal_1.CdmDataTypeDefinition(this.ctx, nameOrRef, undefined);
                    break;
                case internal_1.cdmObjectType.dataTypeRef:
                    newObj = new internal_1.CdmDataTypeReference(this.ctx, nameOrRef, simmpleNameRef);
                    break;
                case internal_1.cdmObjectType.documentDef:
                    newObj = new internal_1.CdmDocumentDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.manifestDef:
                    newObj = new internal_1.CdmManifestDefinition(this.ctx, nameOrRef);
                    // Log and ingest a message when a new manifest is created
                    internal_1.Logger.debug(this.ctx, this.TAG, this.MakeObject.name, newObj.atCorpusPath, "New Manifest created.", true);
                    break;
                case internal_1.cdmObjectType.entityAttributeDef:
                    newObj = new internal_1.CdmEntityAttributeDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.entityDef:
                    newObj = new internal_1.CdmEntityDefinition(this.ctx, nameOrRef, undefined);
                    break;
                case internal_1.cdmObjectType.entityRef:
                    newObj = new internal_1.CdmEntityReference(this.ctx, nameOrRef, simmpleNameRef);
                    break;
                case internal_1.cdmObjectType.import:
                    newObj = new internal_1.CdmImport(this.ctx, nameOrRef, undefined);
                    break;
                case internal_1.cdmObjectType.parameterDef:
                    newObj = new internal_1.CdmParameterDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.purposeDef:
                    newObj = new internal_1.CdmPurposeDefinition(this.ctx, nameOrRef, undefined);
                    break;
                case internal_1.cdmObjectType.purposeRef:
                    newObj = new internal_1.CdmPurposeReference(this.ctx, nameOrRef, simmpleNameRef);
                    break;
                case internal_1.cdmObjectType.traitDef:
                    newObj = new internal_1.CdmTraitDefinition(this.ctx, nameOrRef, undefined);
                    break;
                case internal_1.cdmObjectType.traitRef:
                    newObj = new internal_1.CdmTraitReference(this.ctx, nameOrRef, simmpleNameRef, false);
                    break;
                case internal_1.cdmObjectType.traitGroupDef:
                    newObj = new internal_1.CdmTraitGroupDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.traitGroupRef:
                    newObj = new internal_1.CdmTraitGroupReference(this.ctx, nameOrRef, simmpleNameRef);
                    break;
                case internal_1.cdmObjectType.typeAttributeDef:
                    newObj = new internal_1.CdmTypeAttributeDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.attributeContextDef:
                    newObj = new internal_1.CdmAttributeContext(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.attributeContextRef:
                    newObj = new internal_1.CdmAttributeContextReference(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.attributeRef:
                    newObj = new internal_1.CdmAttributeReference(this.ctx, nameOrRef, simmpleNameRef);
                    break;
                case internal_1.cdmObjectType.dataPartitionDef:
                    newObj = new internal_1.CdmDataPartitionDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.dataPartitionPatternDef:
                    newObj = new internal_1.CdmDataPartitionPatternDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.manifestDeclarationDef:
                    newObj = new internal_1.CdmManifestDeclarationDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.referencedEntityDeclarationDef:
                    newObj = new internal_1.CdmReferencedEntityDeclarationDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.localEntityDeclarationDef:
                    newObj = new internal_1.CdmLocalEntityDeclarationDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.folderDef:
                    newObj = new internal_1.CdmFolderDefinition(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.attributeResolutionGuidanceDef:
                    newObj = new internal_1.CdmAttributeResolutionGuidance(this.ctx);
                    break;
                case internal_1.cdmObjectType.e2eRelationshipDef:
                    newObj = new internal_1.CdmE2ERelationship(this.ctx, nameOrRef);
                    break;
                case internal_1.cdmObjectType.projectionDef:
                    newObj = new internal_1.CdmProjection(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationAddCountAttributeDef:
                    newObj = new internal_1.CdmOperationAddCountAttribute(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationAddSupportingAttributeDef:
                    newObj = new internal_1.CdmOperationAddSupportingAttribute(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationAddTypeAttributeDef:
                    newObj = new internal_1.CdmOperationAddTypeAttribute(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationExcludeAttributesDef:
                    newObj = new internal_1.CdmOperationExcludeAttributes(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationArrayExpansionDef:
                    newObj = new internal_1.CdmOperationArrayExpansion(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationCombineAttributesDef:
                    newObj = new internal_1.CdmOperationCombineAttributes(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationRenameAttributesDef:
                    newObj = new internal_1.CdmOperationRenameAttributes(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationReplaceAsForeignKeyDef:
                    newObj = new internal_1.CdmOperationReplaceAsForeignKey(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationIncludeAttributesDef:
                    newObj = new internal_1.CdmOperationIncludeAttributes(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationAddAttributeGroupDef:
                    newObj = new internal_1.CdmOperationAddAttributeGroup(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationAlterTraitsDef:
                    newObj = new internal_1.CdmOperationAlterTraits(this.ctx);
                    break;
                case internal_1.cdmObjectType.operationAddArtifactAttributeDef:
                    newObj = new internal_1.CdmOperationAddArtifactAttribute(this.ctx);
                    break;
                default:
            }
            return newObj;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    addDocumentObjects(folder, doc) {
        // let bodyCode = () =>
        {
            const path = this.storage.createAbsoluteCorpusPath(`${doc.folderPath}${doc.name}`, doc);
            this.documentLibrary.addDocumentPath(path, folder, doc);
            return doc;
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    removeDocumentObjects(folder, doc) {
        // let bodyCode = () =>
        {
            // every symbol defined in this document is pointing at the document, so remove from cache.
            // also remove the list of docs that it depends on
            this.removeObjectDefinitions(doc);
            // remove from path lookup, folder lookup and global list of documents
            const path = this.storage.createAbsoluteCorpusPath(`${doc.folderPath}${doc.name}`, doc);
            this.documentLibrary.removeDocumentPath(path, folder, doc);
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     */
    indexDocuments(resOpt, loadImports, rootDoc, docsLoaded) {
        const docsNotIndexed = this.documentLibrary.listDocsNotIndexed(rootDoc, docsLoaded);
        // Step: clear document caches.
        for (const doc of docsNotIndexed) {
            if (!doc.declarationsIndexed || loadImports) {
                internal_1.Logger.debug(this.ctx, this.TAG, this.indexDocuments.name, doc.atCorpusPath, `index start: ${doc.atCorpusPath}`);
                doc.clearCaches();
            }
        }
        // Step: check basic integrity.
        for (const doc of docsNotIndexed) {
            if (!doc.declarationsIndexed || loadImports) {
                doc.checkIntegrity();
            }
        }
        // Step: declare definitions in objects in this doc.
        for (const doc of docsNotIndexed) {
            if ((!doc.declarationsIndexed || loadImports) && doc.isValid) {
                doc.declareObjectDefinitions();
            }
        }
        if (loadImports) {
            // Step: Index import priorities.
            for (const doc of docsNotIndexed) {
                if (doc.isValid) {
                    // index any imports
                    doc.getImportPriorities();
                }
            }
            // Step: make sure we can find everything that is named by reference.
            for (const doc of docsNotIndexed) {
                if (doc.isValid) {
                    const resOptLocal = resOpt.copy();
                    resOptLocal.wrtDoc = doc;
                    doc.resolveObjectDefinitions(resOptLocal);
                }
            }
            // Step: now resolve any trait arguments that are type object.
            for (const doc of docsNotIndexed) {
                if (doc.isValid) {
                    const resOptLocal = resOpt.copy();
                    resOptLocal.wrtDoc = doc;
                    doc.resolveTraitArguments(resOptLocal);
                }
            }
        }
        // Step: finish up.
        for (const doc of docsNotIndexed) {
            doc.finishIndexing(loadImports);
        }
        return true;
    }
    /**
     * @deprecated
     */
    visit(path, preChildren, postChildren) {
        return false;
    }
    /**
     * Fetches an object by the path from the corpus.
     * @param objectPath Object path, absolute or relative.
     * @param obj Optional parameter. When provided, it is used to obtain the FolderPath and the Namespace needed
     * to create the absolute path from a relative path.
     * @param shallowValidationOrResOpt Optional parameter. When provided, shallow validation in ResolveOptions is enabled,
     * which logs errors regarding resolving/loading references as warnings.
     * It also accepts a ResolveOptions object. When provided, will use be used to determine how the symbols are resolved.
     * @param forceReload Optional parameter. When true, the document containing the requested object is reloaded from storage
     * to access any external changes made to the document since it may have been cached by the corpus.
     * @returns The object obtained from the provided path.
     */
    fetchObjectAsync(objectPath, obj, shallowValidationOrResOpt = undefined, forceReload = false) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield using_statement_1.using(Logger_1.enterScope(CdmCorpusDefinition.name, this.ctx, this.fetchObjectAsync.name), (_) => __awaiter(this, void 0, void 0, function* () {
                let resOpt;
                if (typeof (shallowValidationOrResOpt) === 'boolean') {
                    resOpt = new internal_1.resolveOptions();
                    resOpt.shallowValidation = shallowValidationOrResOpt;
                }
                else if (shallowValidationOrResOpt === undefined) {
                    resOpt = new internal_1.resolveOptions();
                }
                else {
                    resOpt = shallowValidationOrResOpt;
                }
                objectPath = this.storage.createAbsoluteCorpusPath(objectPath, obj);
                let documentPath = objectPath;
                let documentNameIndex = objectPath.lastIndexOf(this.cdmExtension);
                if (documentNameIndex !== -1) {
                    // if there is something after the document path, split it into document path and object path.
                    documentNameIndex += this.cdmExtension.length;
                    documentPath = objectPath.slice(0, documentNameIndex);
                }
                internal_1.Logger.debug(this.ctx, this.TAG, this.fetchObjectAsync.name, objectPath, `request object: ${objectPath}`);
                const newObj = yield this.documentLibrary.loadFolderOrDocument(documentPath, forceReload);
                if (newObj) {
                    // get imports and index each document that is loaded
                    if (newObj instanceof internal_1.CdmDocumentDefinition) {
                        if (!(yield newObj.indexIfNeeded(resOpt))) {
                            return undefined;
                        }
                        if (!newObj.isValid) {
                            internal_1.Logger.error(this.ctx, this.TAG, this.fetchObjectAsync.name, newObj.atCorpusPath, internal_1.cdmLogCode.ErrValdnInvalidDoc, objectPath);
                            return undefined;
                        }
                    }
                    if (documentPath === objectPath) {
                        if (newObj instanceof internal_1.CdmManifestDefinition) {
                            internal_1.Logger.ingestManifestTelemetry(newObj, this.ctx, this.TAG, this.fetchObjectAsync.name, newObj.atCorpusPath);
                        }
                        return newObj;
                    }
                    if (documentNameIndex === -1) {
                        // there is no remaining path to be loaded, so return.
                        return undefined;
                    }
                    // trim off the document path to get the object path in the doc
                    const remainingObjectPath = objectPath.slice(documentNameIndex + 1);
                    const result = newObj.fetchObjectFromDocumentPath(remainingObjectPath, resOpt);
                    if (result === undefined) {
                        internal_1.Logger.error(this.ctx, this.TAG, this.fetchObjectAsync.name, newObj.atCorpusPath, internal_1.cdmLogCode.ErrDocSymbolNotFound, objectPath, newObj.atCorpusPath);
                    }
                    else {
                        // Log the telemetry if the object is a manifest
                        if (result instanceof internal_1.CdmManifestDefinition) {
                            internal_1.Logger.ingestManifestTelemetry(result, this.ctx, this.TAG, this.fetchObjectAsync.name, result.atCorpusPath);
                        }
                        // Log the telemetry if the object is an entity
                        else if (result instanceof internal_1.CdmEntityDefinition) {
                            internal_1.Logger.ingestEntityTelemetry(result, this.ctx, this.TAG, this.fetchObjectAsync.name, result.atCorpusPath);
                        }
                    }
                    return result;
                }
            }));
        });
    }
    setEventCallback(status, reportAtLevel = internal_1.cdmStatusLevel.info, correlationId) {
        const ctx = this.ctx;
        ctx.statusEvent = status;
        ctx.reportAtLevel = reportAtLevel;
        ctx.correlationId = correlationId;
    }
    /**
     * @internal
     */
    setImportDocuments(doc) {
        if (doc.imports) {
            for (const imp of doc.imports) {
                if (!imp.document) {
                    // no document set for this import, see if it is already loaded into the corpus
                    const path = this.storage.createAbsoluteCorpusPath(imp.corpusPath, doc);
                    const impDoc = this.documentLibrary.fetchDocument(path);
                    if (impDoc) {
                        imp.document = impDoc;
                        this.setImportDocuments(imp.document);
                    }
                }
            }
        }
    }
    /**
     * @internal
     */
    loadImportsAsync(doc, docsLoading, resOpt) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!doc) {
                // if there's not document, our job here is done.
                return;
            }
            const loadDocs = (docPath) => __awaiter(this, void 0, void 0, function* () {
                if (!this.documentLibrary.needToLoadDocument(docPath, docsLoading)) {
                    return;
                }
                // load it
                const loadedDoc = yield this.documentLibrary.loadFolderOrDocument(docPath, false, resOpt);
                if (loadedDoc) {
                    internal_1.Logger.info(this.ctx, this.TAG, this.loadImportsAsync.name, doc.atCorpusPath, `resolved import for '${loadedDoc.name}'`);
                }
                else {
                    internal_1.Logger.warning(this.ctx, this.TAG, this.loadImportsAsync.name, doc.atCorpusPath, internal_1.cdmLogCode.WarnResolveImportFailed, docPath, doc.atCorpusPath);
                }
                yield this.loadImportsAsync(loadedDoc, docsLoading, resOpt);
            });
            // Loop through all of the document's imports and load them recursively.
            const taskList = [];
            for (const imp of doc.imports) {
                if (!imp.document) {
                    const docPath = this.storage.createAbsoluteCorpusPath(imp.corpusPath, doc);
                    const loadTask = loadDocs(docPath);
                    taskList.push(loadTask);
                }
            }
            // Wait for all of the missing docs to finish loading.
            yield Promise.all(taskList);
        });
    }
    /**
     * @internal
     */
    resolveImportsAsync(doc, docsLoading, resOpt) {
        return __awaiter(this, void 0, void 0, function* () {
            // load imports (and imports of imports)
            yield this.loadImportsAsync(doc, docsLoading, resOpt);
            // now that everything is loaded, attach import docs to this doc's import list
            this.setImportDocuments(doc);
        });
    }
    /**
     * @internal
     */
    finishResolve() {
        // let bodyCode = () =>
        {
            const ctx = this.ctx;
            internal_1.Logger.debug(ctx, this.TAG, this.finishResolve.name, undefined, 'finishing...');
            for (const doc of this.documentLibrary.listAllDocuments()) {
                doc.finishIndexing(false);
            }
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     * Returns the last modified time of the file where the object at the corpusPath can be found
     * @param corpusPath The corpus path to a CDM object
     */
    computeLastModifiedTimeAsync(corpusPath, obj) {
        return __awaiter(this, void 0, void 0, function* () {
            const currObject = yield this.fetchObjectAsync(corpusPath, obj, true);
            if (currObject) {
                return this.getLastModifiedTimeFromObjectAsync(currObject);
            }
        });
    }
    /**
     * @internal
     * Returns the last modified time of the file where the input object can be found
     * @param currObject A CDM object
     */
    getLastModifiedTimeFromObjectAsync(currObject) {
        return __awaiter(this, void 0, void 0, function* () {
            if ("namespace" in currObject) {
                const adapter = this.storage.fetchAdapter(currObject.namespace);
                if (adapter === undefined) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.getLastModifiedTimeFromObjectAsync.name, currObject.atCorpusPath, internal_1.cdmLogCode.ErrAdapterNotFound, currObject.namespace);
                    return undefined;
                }
                // Remove namespace from path
                const pathTuple = StorageUtils_1.StorageUtils.splitNamespacePath(currObject.atCorpusPath);
                if (!pathTuple) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.getLastModifiedTimeFromObjectAsync.name, currObject.atCorpusPath, internal_1.cdmLogCode.ErrStorageNullCorpusPath);
                    return undefined;
                }
                try {
                    return adapter.computeLastModifiedTimeAsync(pathTuple[1]);
                }
                catch (e) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.getLastModifiedTimeFromObjectAsync.name, currObject.atCorpusPath, internal_1.cdmLogCode.ErrManifestFileModTimeFailure, pathTuple[1], e.toString());
                    return undefined;
                }
            }
            else {
                return this.getLastModifiedTimeFromObjectAsync(currObject.inDocument);
            }
        });
    }
    /**
     * @internal
     * Returns the last modified time of a partition object, does not try to open the file
     * as getLastModifiedTime does
     * @param corpusPath The corpus path to a CDM object
     */
    getLastModifiedTimeFromPartitionPathAsync(corpusPath) {
        return __awaiter(this, void 0, void 0, function* () {
            // we do not want to load partitions from file, just check the modified times
            const pathTuple = StorageUtils_1.StorageUtils.splitNamespacePath(corpusPath);
            if (!pathTuple) {
                internal_1.Logger.error(this.ctx, this.TAG, this.getLastModifiedTimeFromPartitionPathAsync.name, corpusPath, internal_1.cdmLogCode.ErrPathNullObjectPath);
                return undefined;
            }
            const namespace = pathTuple[0];
            if (namespace) {
                const adapter = this.storage.fetchAdapter(namespace);
                if (adapter === undefined) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.getLastModifiedTimeFromPartitionPathAsync.name, corpusPath, internal_1.cdmLogCode.ErrAdapterNotFound);
                    return undefined;
                }
                try {
                    return adapter.computeLastModifiedTimeAsync(pathTuple[1]);
                }
                catch (e) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.getLastModifiedTimeFromPartitionPathAsync.name, corpusPath, internal_1.cdmLogCode.ErrPartitionFileModTimeFailure, pathTuple[1], e.toString());
                }
            }
            return null;
        });
    }
    /**
     * Returns a list of relationships where the input entity is the incoming entity
     * @param entity The entity that we want to get relationships for
     */
    fetchIncomingRelationships(entity) {
        if (this.incomingRelationships !== undefined && this.incomingRelationships.has(entity.atCorpusPath)) {
            return this.incomingRelationships.get(entity.atCorpusPath);
        }
        return [];
    }
    /**
     * Returns a list of relationships where the input entity is the outgoing entity
     * @param entity The entity that we want to get relationships for
     */
    fetchOutgoingRelationships(entity) {
        if (this.outgoingRelationships !== undefined && this.outgoingRelationships.has(entity.atCorpusPath)) {
            return this.outgoingRelationships.get(entity.atCorpusPath);
        }
        return [];
    }
    /**
     * Calculates the entity to entity relationships for all the entities present in the manifest and its sub-manifests.
     * @param currManifest The manifest (and any sub-manifests it contains) that we want to calculate relationships for.
     * @returns A Promise for the completion of entity graph calculation.
     */
    calculateEntityGraphAsync(currManifest) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield using_statement_1.using(Logger_1.enterScope(CdmCorpusDefinition.name, this.ctx, this.calculateEntityGraphAsync.name), (_) => __awaiter(this, void 0, void 0, function* () {
                for (const entityDec of currManifest.entities) {
                    const entityPath = yield currManifest.getEntityPathFromDeclaration(entityDec, currManifest);
                    // the path returned by GetEntityPathFromDeclaration is an absolute path.
                    // no need to pass the manifest to FetchObjectAsync.
                    const entity = yield this.fetchObjectAsync(entityPath);
                    if (!entity) {
                        continue;
                    }
                    let resEntity;
                    // make options wrt this entity document and "relational" always
                    const resOpt = new internal_1.resolveOptions(entity.inDocument, new internal_1.AttributeResolutionDirectiveSet(new Set(['normalized', 'referenceOnly'])));
                    const isResolvedEntity = entity.isResolved;
                    // only create a resolved entity if the entity passed in was not a resolved entity
                    if (!isResolvedEntity) {
                        // first get the resolved entity so that all of the references are present
                        resEntity = yield entity.createResolvedEntityAsync(`wrtSelf_${entity.entityName}`, resOpt);
                    }
                    else {
                        resEntity = entity;
                    }
                    // find outgoing entity relationships using attribute context
                    const newOutgoingRelationships = this.findOutgoingRelationships(resOpt, resEntity, resEntity.attributeContext, isResolvedEntity);
                    const oldOutgoingRelationships = this.outgoingRelationships.get(entity.atCorpusPath);
                    // fix incoming rels based on any changes made to the outgoing rels
                    if (oldOutgoingRelationships) {
                        for (const rel of oldOutgoingRelationships) {
                            const relString = rel.createCacheKey();
                            const hasRel = newOutgoingRelationships.some(x => x.createCacheKey() === relString);
                            // remove any relationships that no longer exist
                            if (!hasRel) {
                                const targetEnt = yield this.fetchObjectAsync(rel.toEntity, currManifest);
                                if (targetEnt) {
                                    const currIncoming = this.incomingRelationships.get(targetEnt.atCorpusPath);
                                    if (currIncoming) {
                                        currIncoming.splice(currIncoming.indexOf(rel), 1);
                                    }
                                }
                                else {
                                    const absolutePath = this.storage.createAbsoluteCorpusPath(rel.toEntity, rel.inDocument);
                                    this.incomingRelationships.delete(absolutePath);
                                }
                            }
                        }
                    }
                    this.outgoingRelationships.set(entity.atCorpusPath, newOutgoingRelationships);
                    // flip outgoing entity relationships list to get incoming relationships map
                    for (const rel of newOutgoingRelationships) {
                        const targetEnt = yield this.fetchObjectAsync(rel.toEntity, currManifest);
                        if (targetEnt) {
                            if (!this.incomingRelationships.has(targetEnt.atCorpusPath)) {
                                this.incomingRelationships.set(targetEnt.atCorpusPath, []);
                            }
                            this.incomingRelationships.get(targetEnt.atCorpusPath)
                                .push(rel);
                        }
                    }
                    // delete the resolved entity if we created one here
                    if (!isResolvedEntity) {
                        resEntity.inDocument.owner.documents.remove(resEntity.inDocument.name);
                    }
                }
                for (const subManifestDef of currManifest.subManifests) {
                    const corpusPath = this.storage.createAbsoluteCorpusPath(subManifestDef.definition, currManifest);
                    const subManifest = yield this.fetchObjectAsync(corpusPath);
                    if (subManifest) {
                        yield this.calculateEntityGraphAsync(subManifest);
                    }
                }
            }));
        });
    }
    /**
     * @internal
     */
    findOutgoingRelationships(resOpt, resEntity, attCtx, isResolvedEntity = false, generatedAttSetContext, wasProjectionPolymorphic = false, fromAtts = undefined, entityAttAttContext = undefined) {
        var _a, _b;
        let outRels = [];
        if (attCtx && attCtx.contents) {
            // as we traverse the context tree, look for these nodes which hold the foreign key
            // once we find a context node that refers to an entity reference, we will use the
            // nearest _generatedAttributeSet (which is above or at the same level as the entRef context)
            // and use its foreign key
            let newGenSet = attCtx.contents.item('_generatedAttributeSet');
            if (!newGenSet) {
                newGenSet = generatedAttSetContext;
            }
            let isEntityRef = false;
            let isPolymorphicSource = false;
            for (const subAttCtx of attCtx.contents.allItems) {
                if (subAttCtx.objectType === internal_1.cdmObjectType.attributeContextDef) {
                    // find the top level entity definition's attribute context
                    if (entityAttAttContext === undefined && attCtx.type == internal_1.cdmAttributeContextType.attributeDefinition
                        && ((_b = (_a = attCtx.definition) === null || _a === void 0 ? void 0 : _a.fetchObjectDefinition(resOpt)) === null || _b === void 0 ? void 0 : _b.objectType) == internal_1.cdmObjectType.entityAttributeDef) {
                        entityAttAttContext = attCtx;
                    }
                    // find entity references that identifies the 'this' entity
                    const child = subAttCtx;
                    if (child.definition && child.definition.getObjectType() === internal_1.cdmObjectType.entityRef) {
                        const toEntity = child.definition.fetchObjectDefinition(resOpt);
                        if ((toEntity === null || toEntity === void 0 ? void 0 : toEntity.objectType) === internal_1.cdmObjectType.projectionDef) {
                            // Projections
                            const owner = toEntity.owner && toEntity.owner.owner;
                            if (owner) {
                                isPolymorphicSource = (owner.objectType === internal_1.cdmObjectType.entityAttributeDef &&
                                    owner.isPolymorphicSource);
                            }
                            else {
                                internal_1.Logger.error(this.ctx, this.TAG, this.findOutgoingRelationships.name, subAttCtx.atCorpusPath, internal_1.cdmLogCode.ErrObjectWithoutOwnerFound);
                            }
                            // From the top of the projection (or the top most which contains a generatedSet / operations)
                            // get the attribute names for the foreign key
                            if (newGenSet && !fromAtts) {
                                fromAtts = this.getFromAttributes(newGenSet, fromAtts);
                            }
                            // Fetch purpose traits
                            let traitRefsAndCorpusPaths = undefined;
                            const entityAtt = owner.fetchObjectDefinition(resOpt);
                            if ((entityAtt === null || entityAtt === void 0 ? void 0 : entityAtt.purpose) !== undefined) {
                                const resolvedTraitSet = entityAtt.purpose.fetchResolvedTraits(resOpt);
                                if (resolvedTraitSet !== undefined) {
                                    traitRefsAndCorpusPaths = this.findElevatedTraitRefsAndCorpusPaths(resOpt, resolvedTraitSet);
                                }
                            }
                            outRels = this.findOutgoingRelationshipsForProjection(outRels, child, resOpt, resEntity, fromAtts, traitRefsAndCorpusPaths);
                            wasProjectionPolymorphic = isPolymorphicSource;
                        }
                        else {
                            // Non-Projections based approach and current as-is code path
                            isEntityRef = true;
                            const toAtt = child.exhibitsTraits.allItems.filter((x) => {
                                return x.namedReference === 'is.identifiedBy' && x.arguments.length > 0;
                            })
                                .map((y) => {
                                const namedRef = y.arguments.allItems[0].value.namedReference;
                                return namedRef.slice(namedRef.lastIndexOf('/') + 1);
                            });
                            outRels = this.findOutgoingRelationshipsForEntityRef(toEntity, toAtt, outRels, newGenSet, child, resOpt, resEntity, isResolvedEntity, wasProjectionPolymorphic, isEntityRef, entityAttAttContext);
                        }
                    }
                    // repeat the process on the child node
                    const skipAdd = wasProjectionPolymorphic && isEntityRef;
                    const subOutRels = this.findOutgoingRelationships(resOpt, resEntity, child, isResolvedEntity, newGenSet, wasProjectionPolymorphic, fromAtts, entityAttAttContext);
                    outRels = outRels.concat(subOutRels);
                    // if it was a projection-based polymorphic source up through this branch of the tree and currently it has reached the end of the projection tree to come to a non-projection source,
                    // then skip adding just this one source and continue with the rest of the tree
                    if (skipAdd) {
                        // skip adding only this entry in the tree and continue with the rest of the tree
                        wasProjectionPolymorphic = false;
                    }
                }
            }
        }
        return outRels;
    }
    /**
     * Fetch resolved traits on purpose from attribute context (non-projection entity attribute).
     */
    fetchPurposeTraitRefsFromAttCtx(resOpt, attributeCtx) {
        var _a, _b;
        const def = (_a = attributeCtx.definition) === null || _a === void 0 ? void 0 : _a.fetchObjectDefinition(resOpt);
        if ((def === null || def === void 0 ? void 0 : def.objectType) == internal_1.cdmObjectType.entityAttributeDef && ((_b = def) === null || _b === void 0 ? void 0 : _b.purpose) !== undefined) {
            var resolvedTraitSet = def.purpose.fetchResolvedTraits(resOpt);
            if (resolvedTraitSet !== undefined) {
                return this.findElevatedTraitRefsAndCorpusPaths(resOpt, resolvedTraitSet);
            }
        }
        return undefined;
    }
    /**
     * Find the corpus path for each elevated trait.
     */
    findElevatedTraitRefsAndCorpusPaths(resOpt, resolvedTraitSet) {
        const traitRefsAndCorpusPaths = [];
        resolvedTraitSet.set.forEach(rt => {
            var _a;
            var traitRef = internal_1.CdmObjectBase.resolvedTraitToTraitRef(resOpt, rt);
            if (traitRef !== undefined && !internal_1.StringUtils.isNullOrWhiteSpace((_a = rt.trait.inDocument) === null || _a === void 0 ? void 0 : _a.atCorpusPath)) {
                traitRefsAndCorpusPaths.push([traitRef, rt.trait.inDocument.atCorpusPath]);
            }
        });
        return traitRefsAndCorpusPaths;
    }
    /**
     * Find the outgoing relationships for Projections.
     * Given a list of 'From' attributes, find the E2E relationships based on the 'To' information stored in the trait of the attribute in the resolved entity
     * @internal
     */
    findOutgoingRelationshipsForProjection(outRels, child, resOpt, resEntity, fromAtts = undefined, traitRefsAndCorpusPaths = undefined) {
        if (fromAtts) {
            const resOptCopy = resOpt.copy();
            resOptCopy.wrtDoc = resEntity.inDocument;
            // Extract the from entity from resEntity
            const refToLogicalEntity = resEntity.attributeContext.definition;
            const unResolvedEntity = refToLogicalEntity === null || refToLogicalEntity === void 0 ? void 0 : refToLogicalEntity.fetchObjectDefinition(resOptCopy);
            const fromEntity = unResolvedEntity === null || unResolvedEntity === void 0 ? void 0 : unResolvedEntity.ctx.corpus.storage.createRelativeCorpusPath(unResolvedEntity.atCorpusPath, unResolvedEntity.inDocument);
            for (let i = 0; i < fromAtts.length; i++) {
                // List of to attributes from the constant entity argument parameter
                const fromAttrDef = fromAtts[i].fetchObjectDefinition(resOptCopy);
                const tupleList = this.getToAttributes(fromAttrDef, resOptCopy);
                // For each of the to attributes, create a relationship
                for (const tuple of tupleList) {
                    const newE2ERel = new internal_1.CdmE2ERelationship(this.ctx, tuple[2]);
                    newE2ERel.fromEntity = this.storage.createAbsoluteCorpusPath(fromEntity, unResolvedEntity);
                    newE2ERel.fromEntityAttribute = fromAtts[i].fetchObjectDefinitionName();
                    newE2ERel.toEntity = this.storage.createAbsoluteCorpusPath(tuple[0], unResolvedEntity);
                    newE2ERel.toEntityAttribute = tuple[1];
                    this.addTraitRefsAndCorpusPathsToRelationship(traitRefsAndCorpusPaths, newE2ERel);
                    outRels.push(newE2ERel);
                }
            }
        }
        return outRels;
    }
    /**
     * Find the outgoing relationships for Non-Projections EntityRef
     * @internal
     */
    findOutgoingRelationshipsForEntityRef(toEntity, toAtt, outRels, newGenSet, child, resOpt, resEntity, isResolvedEntity, wasProjectionPolymorphic = false, wasEntityRef = false, attributeCtx = undefined) {
        // entity references should have the "is.identifiedBy" trait, and the entity ref should be valid
        if (toAtt.length === 1 && toEntity) {
            // get the attribute name from the foreign key
            const findAddedAttributeIdentity = (context) => {
                if (context && context.contents) {
                    for (const sub of context.contents.allItems) {
                        const subCtx = sub;
                        if (subCtx.type === internal_1.cdmAttributeContextType.entity) {
                            continue;
                        }
                        const fk = findAddedAttributeIdentity(subCtx);
                        if (fk) {
                            return fk;
                        }
                        else if (subCtx && subCtx.type === internal_1.cdmAttributeContextType.addedAttributeIdentity
                            && subCtx.contents && subCtx.contents.allItems[0]) {
                            // entity references should have the "is.identifiedBy" trait, and the entity def should be valid
                            return subCtx.contents.allItems[0].namedReference;
                        }
                    }
                }
            };
            const foreignKey = findAddedAttributeIdentity(newGenSet);
            if (foreignKey) {
                // this list will contain the final tuples used for the toEntity where
                // index 0 is the absolute path to the entity and index 1 is the toEntityAttribute
                const toAttList = [];
                // get the list of toAttributes from the traits on the resolved attribute
                const resolvedResOpt = new internal_1.resolveOptions(resEntity.inDocument);
                const attFromFk = this.resolveSymbolReference(resolvedResOpt, resEntity.inDocument, foreignKey, internal_1.cdmObjectType.typeAttributeDef, false);
                if (attFromFk !== undefined) {
                    const fkArgValues = this.getToAttributes(attFromFk, resolvedResOpt);
                    for (const constEnt of fkArgValues) {
                        const absolutePath = this.storage.createAbsoluteCorpusPath(constEnt[0], attFromFk);
                        toAttList.push([absolutePath, constEnt[1]]);
                    }
                }
                const traitRefsAndCorpusPaths = this.fetchPurposeTraitRefsFromAttCtx(resOpt, attributeCtx);
                for (const attributeTuple of toAttList) {
                    const fromAtt = foreignKey.slice(foreignKey.lastIndexOf('/') + 1)
                        .replace(`${child.name}_`, '');
                    const newE2ERel = new internal_1.CdmE2ERelationship(this.ctx, '');
                    newE2ERel.fromEntityAttribute = fromAtt;
                    newE2ERel.toEntityAttribute = attributeTuple[1];
                    this.addTraitRefsAndCorpusPathsToRelationship(traitRefsAndCorpusPaths, newE2ERel);
                    if (isResolvedEntity) {
                        newE2ERel.fromEntity = resEntity.atCorpusPath;
                        if (this.resEntMap.has(attributeTuple[0])) {
                            newE2ERel.toEntity = this.resEntMap.get(attributeTuple[0]);
                        }
                        else {
                            newE2ERel.toEntity = attributeTuple[0];
                        }
                    }
                    else {
                        // find the path of the unresolved entity using the attribute context of the resolved entity
                        const refToLogicalEntity = resEntity.attributeContext.definition;
                        let unResolvedEntity;
                        if (refToLogicalEntity) {
                            unResolvedEntity = refToLogicalEntity.fetchObjectDefinition(resOpt);
                        }
                        const selectedEntity = unResolvedEntity !== undefined ? unResolvedEntity : resEntity;
                        const selectedEntCorpusPath = unResolvedEntity !== undefined ? unResolvedEntity.atCorpusPath : resEntity.atCorpusPath.replace('wrtSelf_', '');
                        newE2ERel.fromEntity = this.storage.createAbsoluteCorpusPath(selectedEntCorpusPath, selectedEntity);
                        newE2ERel.toEntity = attributeTuple[0];
                    }
                    // if it was a projection-based polymorphic source up through this branch of the tree and currently it has reached the end of the projection tree to come to a non-projection source,
                    // then skip adding just this one source and continue with the rest of the tree
                    if (!(wasProjectionPolymorphic && wasEntityRef)) {
                        outRels.push(newE2ERel);
                    }
                }
            }
        }
        return outRels;
    }
    addTraitRefsAndCorpusPathsToRelationship(traitRefsAndCorpusPaths, cdmE2ERel) {
        if (traitRefsAndCorpusPaths !== undefined) {
            for (const pair of traitRefsAndCorpusPaths) {
                cdmE2ERel.exhibitsTraits.allItems.push(pair[0]);
                cdmE2ERel.getElevatedTraitCorpusPath().set(pair[0], pair[1]);
            }
        }
    }
    /**
     * Resolves references according to the provided stages and validates.
     * @returns The validation step that follows the completed step
     * @deprecated
     */
    resolveReferencesAndValidateAsync(stage, stageThrough, resOpt) {
        return __awaiter(this, void 0, void 0, function* () {
            // let bodyCode = () =>
            {
                return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
                    // use the provided directives or use the current default
                    let directives;
                    if (resOpt) {
                        directives = resOpt.directives;
                    }
                    else {
                        directives = this.defaultResolutionDirectives;
                    }
                    resOpt = new internal_1.resolveOptions(undefined, directives);
                    resOpt.depthInfo.reset();
                    for (const doc of this.documentLibrary.listAllDocuments()) {
                        yield doc.indexIfNeeded(resOpt);
                    }
                    const finishresolve = stageThrough === stage;
                    switch (stage) {
                        case internal_1.cdmValidationStep.start:
                        case internal_1.cdmValidationStep.traitAppliers:
                            this.resolveReferencesStep('defining traits...', (currentDoc, resOptions, entityNesting) => { }, resOpt, true, finishresolve || stageThrough === internal_1.cdmValidationStep.minimumForResolving, internal_1.cdmValidationStep.traits);
                            return;
                        case internal_1.cdmValidationStep.traits:
                            this.resolveReferencesStep('resolving traits...', (currentDoc, resOptions, entityNesting) => {
                                this.resolveTraits(currentDoc, resOptions, entityNesting);
                            }, resOpt, false, finishresolve, internal_1.cdmValidationStep.traits);
                            this.resolveReferencesStep('checking required arguments...', (currentDoc, resOptions, entityNesting) => {
                                this.resolveReferencesTraitsArguments(currentDoc, resOptions, entityNesting);
                            }, resOpt, true, finishresolve, internal_1.cdmValidationStep.attributes);
                            return;
                        case internal_1.cdmValidationStep.attributes:
                            this.resolveReferencesStep('resolving attributes...', (currentDoc, resOptions, entityNesting) => {
                                this.resolveAttributes(currentDoc, resOptions, entityNesting);
                            }, resOpt, true, finishresolve, internal_1.cdmValidationStep.entityReferences);
                            return;
                        case internal_1.cdmValidationStep.entityReferences:
                            this.resolveReferencesStep('resolving foreign key references...', (currentDoc, resOptions, entityNesting) => {
                                this.resolveForeignKeyReferences(currentDoc, resOptions, entityNesting);
                            }, resOpt, true, true, internal_1.cdmValidationStep.finished);
                            return;
                        default:
                    }
                    // bad step sent in
                    resolve(internal_1.cdmValidationStep.error);
                }));
            }
        });
    }
    /**
     * @internal
     * fetches from primitives or creates the default attributes that get added by resolution
     */
    prepareArtifactAttributesAsync() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.knownArtifactAttributes) {
                this.knownArtifactAttributes = new Map();
                // see if we can get the value from primitives doc
                // this might fail, and we do not want the user to know about it.
                const oldStatus = this.ctx.statusEvent; // todo, we should make an easy way for our code to do this and set it back
                const oldLevel = this.ctx.reportAtLevel;
                this.setEventCallback(() => { }, internal_1.cdmStatusLevel.error);
                let entArt;
                try {
                    entArt = yield this.fetchObjectAsync('cdm:/primitives.cdm.json/defaultArtifacts');
                }
                finally {
                    this.setEventCallback(oldStatus, oldLevel);
                }
                if (!entArt) {
                    // fallback to the old ways, just make some
                    let artAtt = this.MakeObject(internal_1.cdmObjectType.typeAttributeDef, 'count');
                    artAtt.dataType = this.MakeObject(internal_1.cdmObjectType.dataTypeRef, 'integer', true);
                    this.knownArtifactAttributes.set('count', artAtt);
                    artAtt = this.MakeObject(internal_1.cdmObjectType.typeAttributeDef, 'id');
                    artAtt.dataType = this.MakeObject(internal_1.cdmObjectType.dataTypeRef, 'entityId', true);
                    this.knownArtifactAttributes.set('id', artAtt);
                    artAtt = this.MakeObject(internal_1.cdmObjectType.typeAttributeDef, 'type');
                    artAtt.dataType = this.MakeObject(internal_1.cdmObjectType.dataTypeRef, 'entityName', true);
                    this.knownArtifactAttributes.set('type', artAtt);
                }
                else {
                    // point to the ones from the file
                    for (const att of entArt.attributes) {
                        this.knownArtifactAttributes.set(att.name, att);
                    }
                }
            }
            return true;
        });
    }
    /**
     * @internal
     * returns the (previously prepared) artifact attribute of the known name
     */
    fetchArtifactAttribute(name) {
        if (!this.knownArtifactAttributes) {
            // this is a usage mistake. never call this before success from the PrepareArtifactAttributesAsync
            return undefined;
        }
        return this.knownArtifactAttributes.get(name).copy();
    }
    removeObjectDefinitions(doc) {
        // let bodyCode = () =>
        {
            const ctx = this.ctx;
            doc.visit('', (iObject, path) => {
                if (path.indexOf('(unspecified)') !== -1) {
                    return true;
                }
                switch (iObject.objectType) {
                    case internal_1.cdmObjectType.entityDef:
                    case internal_1.cdmObjectType.parameterDef:
                    case internal_1.cdmObjectType.traitDef:
                    case internal_1.cdmObjectType.traitGroupDef:
                    case internal_1.cdmObjectType.purposeDef:
                    case internal_1.cdmObjectType.dataTypeDef:
                    case internal_1.cdmObjectType.typeAttributeDef:
                    case internal_1.cdmObjectType.entityAttributeDef:
                    case internal_1.cdmObjectType.attributeGroupDef:
                    case internal_1.cdmObjectType.constantEntityDef:
                    case internal_1.cdmObjectType.attributeContextDef:
                    case internal_1.cdmObjectType.localEntityDeclarationDef:
                    case internal_1.cdmObjectType.referencedEntityDeclarationDef:
                    case internal_1.cdmObjectType.projectionDef:
                    case internal_1.cdmObjectType.operationAddCountAttributeDef:
                    case internal_1.cdmObjectType.operationAddSupportingAttributeDef:
                    case internal_1.cdmObjectType.operationAddTypeAttributeDef:
                    case internal_1.cdmObjectType.operationExcludeAttributesDef:
                    case internal_1.cdmObjectType.operationArrayExpansionDef:
                    case internal_1.cdmObjectType.operationCombineAttributesDef:
                    case internal_1.cdmObjectType.operationRenameAttributesDef:
                    case internal_1.cdmObjectType.operationReplaceAsForeignKeyDef:
                    case internal_1.cdmObjectType.operationIncludeAttributesDef:
                    case internal_1.cdmObjectType.operationAddAttributeGroupDef:
                    case internal_1.cdmObjectType.operationAlterTraitsDef:
                    case internal_1.cdmObjectType.operationAddArtifactAttributeDef:
                        this.unRegisterSymbol(path, doc);
                        this.unRegisterDefinitionReferenceDocuments(iObject, 'rasb');
                    default:
                }
                return false;
            }, undefined);
        }
        // return p.measure(bodyCode);
    }
    /**
     * @internal
     * @param symbolDef
     * @param inDoc
     */
    registerSymbol(symbolDef, inDoc) {
        // let bodyCode = () =>
        {
            let docs = this.symbolDefinitions.get(symbolDef);
            if (!docs) {
                docs = [];
                this.symbolDefinitions.set(symbolDef, docs);
            }
            docs.push(inDoc);
        }
        // return p.measure(bodyCode);
    }
    unRegisterSymbol(symbolDef, inDoc) {
        // let bodyCode = () =>
        {
            // if the symbol is listed for the given doc, remove it
            const docs = this.symbolDefinitions.get(symbolDef);
            if (docs) {
                const index = docs.indexOf(inDoc);
                if (index !== -1) {
                    docs.splice(index, 1);
                }
            }
        }
        // return p.measure(bodyCode);
    }
    resolveTraits(currentDoc, resOpt, entityNesting) {
        const ctx = this.ctx;
        currentDoc.visit('', (iObject, path) => {
            switch (iObject.objectType) {
                case internal_1.cdmObjectType.entityDef:
                case internal_1.cdmObjectType.attributeGroupDef:
                    entityNesting++;
                    // don't do this for entities and groups defined within entities since getting traits already does that
                    if (entityNesting > 1) {
                        break;
                    }
                case internal_1.cdmObjectType.traitDef:
                case internal_1.cdmObjectType.traitGroupDef:
                case internal_1.cdmObjectType.purposeDef:
                case internal_1.cdmObjectType.dataTypeDef:
                    this.ctx.relativePath = path;
                    iObject.fetchResolvedTraits(resOpt);
                    break;
                case internal_1.cdmObjectType.entityAttributeDef:
                case internal_1.cdmObjectType.typeAttributeDef:
                    ctx.relativePath = path;
                    iObject.fetchResolvedTraits(resOpt);
                default:
            }
            return false;
        }, (iObject, path) => {
            if (cdmObjectTypeGuards_1.isEntityDefinition(iObject) || cdmObjectTypeGuards_1.isAttributeGroupDefinition(iObject)) {
                entityNesting--;
            }
            return false;
        });
    }
    resolveReferencesTraitsArguments(currentDoc, resOpt, entityNesting) {
        const ctx = this.ctx;
        const checkRequiredParamsOnResolvedTraits = (obj) => {
            const rts = obj.fetchResolvedTraits(resOpt);
            if (rts) {
                const l = rts.size;
                for (let i = 0; i < l; i++) {
                    const rt = rts.set[i];
                    let found = 0;
                    let resolved = 0;
                    if (rt && rt.parameterValues) {
                        const parameterValuesCount = rt.parameterValues.length;
                        for (let iParam = 0; iParam < parameterValuesCount; iParam++) {
                            if (rt.parameterValues.fetchParameterAtIndex(iParam)
                                .getRequired()) {
                                found++;
                                if (!rt.parameterValues.fetchValue(iParam)) {
                                    const paramName = rt.parameterValues.fetchParameterAtIndex(iParam)
                                        .getName();
                                    const objectName = obj.fetchObjectDefinition(resOpt)
                                        .getName();
                                    internal_1.Logger.error(this.ctx, this.TAG, this.resolveReferencesTraitsArguments.name, currentDoc.atCorpusPath, internal_1.cdmLogCode.ErrTraitArgumentMissing, paramName, rt.traitName, objectName);
                                }
                                else {
                                    resolved++;
                                }
                            }
                        }
                    }
                    if (found > 0 && found === resolved) {
                        internal_1.Logger.info(ctx, this.TAG, this.resolveReferencesTraitsArguments.name, currentDoc.atCorpusPath, `found and resolved '${found}' required parameters of trait '${rt.traitName}' on '${obj
                            .fetchObjectDefinition(resOpt)
                            .getName()}'`);
                    }
                }
            }
        };
        currentDoc.visit('', undefined, (iObject, path) => {
            const ot = iObject.objectType;
            if (ot === internal_1.cdmObjectType.entityDef) {
                ctx.relativePath = path;
                // get the resolution of all parameters and values through inheritence and defaults and arguments, etc.
                checkRequiredParamsOnResolvedTraits(iObject);
                // do the same for all attributes
                if (iObject.attributes) {
                    for (const attDef of iObject.attributes) {
                        checkRequiredParamsOnResolvedTraits(attDef);
                    }
                }
            }
            if (ot === internal_1.cdmObjectType.attributeGroupDef) {
                ctx.relativePath = path;
                // get the resolution of all parameters and values through inheritence and defaults and arguments, etc.
                checkRequiredParamsOnResolvedTraits(iObject);
                // do the same for all attributes
                if (iObject.members) {
                    for (const attDef of iObject.members) {
                        checkRequiredParamsOnResolvedTraits(attDef);
                    }
                }
            }
            return false;
        });
    }
    resolveAttributes(currentDoc, resOpt, entityNesting) {
        const ctx = this.ctx;
        currentDoc.visit('', (iObject, path) => {
            const ot = iObject.objectType;
            if (ot === internal_1.cdmObjectType.entityDef) {
                entityNesting++; // get resolved att is already recursive, so don't compound
                if (entityNesting === 1) {
                    ctx.relativePath = path;
                    iObject.fetchResolvedAttributes(resOpt);
                }
            }
            if (ot === internal_1.cdmObjectType.attributeGroupDef) {
                entityNesting++;
                if (entityNesting === 1) {
                    // entity will do this for the group defined inside it
                    ctx.relativePath = path;
                    iObject.fetchResolvedAttributes(resOpt);
                }
            }
            return false;
        }, (iObject, path) => {
            if (cdmObjectTypeGuards_1.isEntityDefinition(iObject) || cdmObjectTypeGuards_1.isAttributeGroupDefinition(iObject)) {
                entityNesting--;
            }
            return false;
        });
    }
    resolveForeignKeyReferences(currentDoc, resOpt, entityNesting) {
        currentDoc.visit('', (iObject, path) => {
            const ot = iObject.objectType;
            if (ot === internal_1.cdmObjectType.attributeGroupDef) {
                entityNesting++;
            }
            if (ot === internal_1.cdmObjectType.entityDef) {
                entityNesting++;
                if (entityNesting === 1) {
                    // get resolved is recursive, so no need
                    this.ctx.relativePath = path;
                    iObject.fetchResolvedEntityReference(resOpt);
                }
            }
            return false;
        }, (iObject, path) => {
            if (cdmObjectTypeGuards_1.isEntityDefinition(iObject) || cdmObjectTypeGuards_1.isAttributeGroupDefinition(iObject)) {
                entityNesting--;
            }
            return false;
        });
    }
    resolveReferencesStep(statusMessage, resolveAction, resolveOpt, stageFinished, finishResolve, nextStage) {
        const ctx = this.ctx;
        internal_1.Logger.debug(ctx, this.TAG, this.resolveReferencesStep.name, undefined, statusMessage);
        const entityNesting = 0;
        for (const doc of this.documentLibrary.listAllDocuments()) {
            // cache import documents
            const currentDoc = doc;
            resolveOpt.wrtDoc = currentDoc;
            resolveAction(currentDoc, resolveOpt, entityNesting);
        }
        if (stageFinished) {
            if (finishResolve) {
                this.finishResolve();
                return internal_1.cdmValidationStep.finished;
            }
            return nextStage;
        }
        return nextStage;
    }
    // Generates the warnings for a single document.
    generateWarningsForSingleDoc(doc, resOpt) {
        return __awaiter(this, void 0, void 0, function* () {
            if (doc.getDefinitions() === undefined) {
                return;
            }
            const ctx = this.ctx;
            resOpt.wrtDoc = doc;
            // check if primary key is missing and report if so
            yield Promise.all(doc.definitions.allItems
                .map((element) => __awaiter(this, void 0, void 0, function* () {
                if (element instanceof internal_1.CdmEntityDefinition && (element).attributes !== undefined) {
                    const resolvedEntity = yield element.createResolvedEntityAsync(`${element.entityName}_`, resOpt);
                    // tslint:disable-next-line:no-suspicious-comment
                    // TODO: Add additional checks here.
                    this.checkPrimaryKeyAttributes(resolvedEntity, resOpt, ctx);
                }
            })));
            resOpt.wrtDoc = undefined;
        });
    }
    /**
     * Checks whether a resolved entity has an "is.identifiedBy" trait.
     */
    checkPrimaryKeyAttributes(resolvedEntity, resOpt, ctx) {
        if (resolvedEntity.fetchResolvedTraits(resOpt)
            .find(resOpt, 'is.identifiedBy') === undefined) {
            internal_1.Logger.warning(this.ctx, this.TAG, this.checkPrimaryKeyAttributes.name, resolvedEntity.atCorpusPath, internal_1.cdmLogCode.WarnValdnPrimaryKeyMissing, resolvedEntity.getName());
        }
    }
    reportErrorStatus(found, symbolDef, expectedType) {
        const ctx = this.ctx;
        switch (expectedType) {
            case internal_1.cdmObjectType.traitRef:
                if (!cdmObjectTypeGuards_1.isCdmTraitDefinition(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'trait', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.dataTypeRef:
                if (!cdmObjectTypeGuards_1.isDataTypeDefinition(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'dataType', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.entityRef:
                if (!cdmObjectTypeGuards_1.isEntityDefinition(found) && !cdmObjectTypeGuards_1.isProjection(found) && !cdmObjectTypeGuards_1.isConstantEntityDefinition(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'entity or type projection or type constant entity', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.parameterDef:
                if (!cdmObjectTypeGuards_1.isParameterDefinition(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'parameter', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.purposeRef:
                if (!cdmObjectTypeGuards_1.isPurposeDefinition(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'purpose', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.traitGroupRef:
                if (!cdmObjectTypeGuards_1.isCdmTraitGroupDefinition(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'traitGroup', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.attributeGroupRef:
                if (!cdmObjectTypeGuards_1.isAttributeGroupDefinition(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'attributeGroup', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.projectionDef:
                if (!cdmObjectTypeGuards_1.isProjection(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'projection', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationAddCountAttributeDef:
                if (!cdmObjectTypeGuards_1.isOperationAddCountAttribute(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'add count attribute operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationAddSupportingAttributeDef:
                if (!cdmObjectTypeGuards_1.isOperationAddSupportingAttribute(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'add supporting attribute operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationAddTypeAttributeDef:
                if (!cdmObjectTypeGuards_1.isOperationAddTypeAttribute(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'add type attribute operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationExcludeAttributesDef:
                if (!cdmObjectTypeGuards_1.isOperationExcludeAttributes(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'exclude attributes operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationArrayExpansionDef:
                if (!cdmObjectTypeGuards_1.isOperationArrayExpansion(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'array expansion operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationCombineAttributesDef:
                if (!cdmObjectTypeGuards_1.isOperationCombineAttributes(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'combine attributes operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationRenameAttributesDef:
                if (!cdmObjectTypeGuards_1.isOperationRenameAttributes(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'rename attributes operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationReplaceAsForeignKeyDef:
                if (!cdmObjectTypeGuards_1.isOperationReplaceAsForeignKey(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'replace as foreign key operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationIncludeAttributesDef:
                if (!cdmObjectTypeGuards_1.isOperationIncludeAttributes(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'include attributes operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationAddAttributeGroupDef:
                if (!cdmObjectTypeGuards_1.isOperationAddAttributeGroup(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'add attribute group operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationAlterTraitsDef:
                if (!cdmObjectTypeGuards_1.isOperationAlterTraits(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'alter traits operation', symbolDef);
                    return undefined;
                }
                break;
            case internal_1.cdmObjectType.operationAddArtifactAttributeDef:
                if (!cdmObjectTypeGuards_1.isOperationAddArtifactAttribute(found)) {
                    internal_1.Logger.error(this.ctx, this.TAG, this.reportErrorStatus.name, found.atCorpusPath, internal_1.cdmLogCode.ErrUnexpectedType, 'add artifact attribute operation', symbolDef);
                    return undefined;
                }
                break;
            default:
        }
        return found;
    }
    isPathManifestDocument(path) {
        return path.endsWith(internal_1.CdmConstants.manifestExtension)
            || path.endsWith(internal_1.CdmConstants.modelJsonExtension)
            || path.endsWith(internal_1.CdmConstants.folioExtension);
    }
    pathToSymbol(symbol, docFrom, docResultTo) {
        // if no destination given, no path to look for
        if (docResultTo.docBest === undefined) {
            return undefined;
        }
        // if there, return
        if (docFrom === docResultTo.docBest) {
            return docResultTo.newSymbol;
        }
        // if the to Doc is imported directly here,
        let pri;
        if (docFrom.importPriorities.importPriority.has(docResultTo.docBest)) {
            pri = docFrom.importPriorities.importPriority.get(docResultTo.docBest).priority;
            // if the imported version is the highest priority, we are good
            if (!docResultTo.docList || docResultTo.docList.length === 1) {
                return symbol;
            }
            // more than 1 symbol, see if highest pri
            let maxPri = -1;
            for (const docCheck of docResultTo.docList) {
                const priCheck = docFrom.importPriorities.importPriority.get(docCheck).priority;
                if (priCheck > maxPri) {
                    maxPri = priCheck;
                }
            }
            if (maxPri === pri) {
                return symbol;
            }
        }
        // can't get there directly, check the monikers
        if (docFrom.importPriorities.monikerPriorityMap !== undefined) {
            for (const kv of docFrom.importPriorities.monikerPriorityMap) {
                const tryMoniker = this.pathToSymbol(symbol, kv[1], docResultTo);
                if (tryMoniker !== undefined) {
                    return `${kv[0]}/${tryMoniker}`;
                }
            }
        }
        return undefined;
    }
    /**
     * For Projections get the list of 'From' Attributes
     */
    getFromAttributes(newGenSet, fromAttrs) {
        if (newGenSet === null || newGenSet === void 0 ? void 0 : newGenSet.contents) {
            if (!fromAttrs) {
                fromAttrs = [];
            }
            for (const sub of newGenSet.contents) {
                if (sub.objectType === internal_1.cdmObjectType.attributeContextDef) {
                    const subCtx = sub;
                    fromAttrs = this.getFromAttributes(subCtx, fromAttrs);
                }
                else if (sub.objectType === internal_1.cdmObjectType.attributeRef) {
                    fromAttrs.push(sub);
                }
            }
        }
        return fromAttrs;
    }
    /**
     * For Projections get the list of 'To' Attributes
     */
    getToAttributes(fromAttrDef, resOpt) {
        if (fromAttrDef === null || fromAttrDef === void 0 ? void 0 : fromAttrDef.appliedTraits) {
            const tupleList = [];
            for (const trait of fromAttrDef.appliedTraits) {
                if (trait.namedReference === 'is.linkedEntity.identifier' && trait.arguments.length > 0) {
                    const constEnt = trait.arguments.allItems[0].value
                        .fetchObjectDefinition(resOpt);
                    if (constEnt && constEnt.constantValues.length > 0) {
                        for (const constantValues of constEnt.constantValues) {
                            tupleList.push([constantValues[0], constantValues[1], constantValues.length > 2 ? constantValues[2] : '']);
                        }
                    }
                }
            }
            return tupleList;
        }
        return undefined;
    }
}
exports.CdmCorpusDefinition = CdmCorpusDefinition;
/**
 * @internal
 */
// tslint:disable-next-line:variable-name
CdmCorpusDefinition._nextID = 0;
//# sourceMappingURL=CdmCorpusDefinition.js.map