internal CdmObjectBase ResolveSymbolReference()

in objectModel/CSharp/Microsoft.CommonDataModel.ObjectModel/Cdm/CdmCorpusDefinition.cs [257:531]


        internal CdmObjectBase ResolveSymbolReference(ResolveOptions resOpt, CdmDocumentDefinition fromDoc, string symbolDef, CdmObjectType expectedType, bool retry)
        {
            ResolveContext ctx = this.Ctx as ResolveContext;

            // given a symbolic name, find the 'highest prirority' 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?.WrtDoc == null || symbolDef == null)
            {
                return null; // no way to figure this out
            }
            CdmDocumentDefinition wrtDoc = resOpt.WrtDoc;

            if (wrtDoc.NeedsIndexing && !wrtDoc.CurrentlyIndexing)
            {
                var indexTask = Task.Run(async () => await wrtDoc.IndexIfNeeded(resOpt, true));

                // if the wrtDoc needs to be indexed (like it was just modified) then do that first
                if (!indexTask.Result)
                {
                    Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), wrtDoc.AtCorpusPath, CdmLogCode.ErrIndexFailed);
                    return null;
                }
            }

            if (wrtDoc.NeedsIndexing && resOpt.ImportsLoadStrategy == ImportsLoadStrategy.DoNotLoad)
            {
                Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), wrtDoc.AtCorpusPath, CdmLogCode.ErrSymbolNotFound, symbolDef, "because the ImportsLoadStrategy is set to DoNotLoad");
                return null;
            }

            // save the symbol name as it got here
            string initialSymbol = symbolDef;

            // when trying to find a reference, first find the definition that contains it
            // and then look for the reference inside it.
            bool isReference = symbolDef?.EndsWith("(ref)") == true;
            if (isReference)
            {
                int defIndex = symbolDef.IndexOf("/");
                symbolDef = symbolDef.Substring(0, defIndex);
            }

            // get the array of documents where the symbol is defined
            DocsResult symbolDocsResult = this.DocsForSymbol(resOpt, wrtDoc, fromDoc, symbolDef);
            CdmDocumentDefinition docBest = symbolDocsResult.DocBest;
            symbolDef = symbolDocsResult.NewSymbol;

            if (!isReference)
            {
                initialSymbol = symbolDef;
            }

            List<CdmDocumentDefinition> docs = symbolDocsResult.DocList;
            if (docs != null)
            {
                // add this symbol to the set being collected in resOpt, we will need this when caching
                if (resOpt.SymbolRefSet == null)
                {
                    resOpt.SymbolRefSet = new 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 == null)
                {
                    return null;
                }

                IDictionary<CdmDocumentDefinition, ImportInfo> importPriority = wrtDoc.ImportPriorities.ImportPriority;
                if (importPriority.Count == 0)
                {
                    return null;
                }


                if (docBest == null)
                {
                    docBest = FetchPriorityDocument(docs, importPriority);
                }
            }

            // perhaps we have never heard of this symbol in the imports for this document?
            if (docBest == null)
            {
                return null;
            }

            // return the definition found in the best document
            docBest.InternalDeclarations.TryGetValue(symbolDef, out CdmObjectBase found);

            // 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 != null && isReference)
            {
                CdmObjectBase foundRef = null;
                // try to find the reference
                found.Visit("", new VisitCallback
                {
                    Invoke = (obj, objPath) =>
                    {
                        if (string.Equals(initialSymbol, objPath))
                        {
                            foundRef = obj as CdmObjectBase;
                            return true;
                        }
                        return false;
                    }
                }, null);
                found = foundRef;
            }

            if (found == null && 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, retry: false);
            }

            if (found != null && expectedType != CdmObjectType.Error)
            {
                switch (expectedType)
                {
                    case CdmObjectType.TraitRef:
                        if (found.ObjectType != CdmObjectType.TraitDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "trait", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.DataTypeRef:
                        if (found.ObjectType != CdmObjectType.DataTypeDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "dataType", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.EntityRef:
                        if (found.ObjectType != CdmObjectType.EntityDef && found.ObjectType != CdmObjectType.ProjectionDef && found.ObjectType != CdmObjectType.ConstantEntityDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "entity or type projection or type constant entity", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.ParameterDef:
                        if (found.ObjectType != CdmObjectType.ParameterDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "parameter", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.PurposeRef:
                        if (found.ObjectType != CdmObjectType.PurposeDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "purpose", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.TraitGroupRef:
                        if (found.ObjectType != CdmObjectType.TraitGroupDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "traitGroup", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.AttributeGroupRef:
                        if (found.ObjectType != CdmObjectType.AttributeGroupDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "attributeGroup", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.ProjectionDef:
                        if (found.ObjectType != CdmObjectType.ProjectionDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "projection", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationAddCountAttributeDef:
                        if (found.ObjectType != CdmObjectType.OperationAddCountAttributeDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "add count attribute operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationAddSupportingAttributeDef:
                        if (found.ObjectType != CdmObjectType.OperationAddSupportingAttributeDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "add supporting attribute operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationAddTypeAttributeDef:
                        if (found.ObjectType != CdmObjectType.OperationAddTypeAttributeDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "type attribute operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationExcludeAttributesDef:
                        if (found.ObjectType != CdmObjectType.OperationExcludeAttributesDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "exclude attributes operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationArrayExpansionDef:
                        if (found.ObjectType != CdmObjectType.OperationArrayExpansionDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "array expansion operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationCombineAttributesDef:
                        if (found.ObjectType != CdmObjectType.OperationCombineAttributesDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "combine attributes operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationRenameAttributesDef:
                        if (found.ObjectType != CdmObjectType.OperationRenameAttributesDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "rename attributes operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationReplaceAsForeignKeyDef:
                        if (found.ObjectType != CdmObjectType.OperationReplaceAsForeignKeyDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "replace as foreign key operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationIncludeAttributesDef:
                        if (found.ObjectType != CdmObjectType.OperationIncludeAttributesDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "include attributes operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationAddAttributeGroupDef:
                        if (found.ObjectType != CdmObjectType.OperationAddAttributeGroupDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "add attribute group operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationAlterTraitsDef:
                        if (found.ObjectType != CdmObjectType.OperationAlterTraitsDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "alter traits operation", symbolDef);
                            found = null;
                        }
                        break;
                    case CdmObjectType.OperationAddArtifactAttributeDef:
                        if (found.ObjectType != CdmObjectType.OperationAddArtifactAttributeDef)
                        {
                            Logger.Error(ctx, Tag, nameof(ResolveSymbolReference), found.AtCorpusPath, CdmLogCode.ErrUnexpectedType, "add artifact attribute operation", symbolDef);
                            found = null;
                        }
                        break;
                }
            }

            if (resOpt.SymbolRefToObjects != null && found is CdmObjectDefinitionBase foundDef)
            {
                resOpt.SymbolRefToObjects.Add(Tuple.Create(symbolDef, foundDef));
            }

            return found;
        }