using System; using System.Collections.Generic; namespace BismNormalizer.TabularCompare.TabularMetadata { [Serializable] public class CalcDependenciesInfiniteRecursionException : Exception { public CalcDependenciesInfiniteRecursionException() { } public CalcDependenciesInfiniteRecursionException(string message) : base(message) { } public CalcDependenciesInfiniteRecursionException(string message, Exception inner) : base(message, inner) { } } /// /// Represents a collection of CalcDependency objects. /// public class CalcDependencyCollection : List { /// /// Returns collection of calc dependencies that the object identified by the params references (directly or indirectly). /// /// Type of the object to look up dependencies. /// Name of the object to look up dependencies. /// public CalcDependencyCollection DependenciesReferenceFrom(CalcDependencyObjectType objectType, string tableName, string objectName) { CalcDependencyCollection returnVal = new CalcDependencyCollection(); recursionCounter = 0; try { LookUpDependenciesReferenceFrom(objectType, tableName, objectName, returnVal); } catch (CalcDependenciesInfiniteRecursionException) { //Some M is not parsable } return returnVal; } private const int RecursionCounterMax = 1000; static int recursionCounter = 0; private void LookUpDependenciesReferenceFrom(CalcDependencyObjectType objectType, string tableName, string objectName, CalcDependencyCollection returnVal) { recursionCounter++; if (recursionCounter >= RecursionCounterMax) { Exception infRecursion = new CalcDependenciesInfiniteRecursionException($"Calc dependencies infinite recursion detected for type \"{objectType}\", table \"{tableName}\", name \"{objectName}\"."); //Telemetry.TrackException(infRecursion); throw infRecursion; } foreach (CalcDependency calcDependency in this) { if (calcDependency.ObjectType == objectType && calcDependency.TableName == tableName && calcDependency.ObjectName == objectName) { LookUpDependenciesReferenceFrom(calcDependency.ReferencedObjectType, calcDependency.ReferencedTableName, calcDependency.ReferencedObjectName, returnVal); returnVal.Add(calcDependency); } } } /// /// Returns collection of M dependency objects that hold references to the object identified by the param values (directly or chained). /// /// Type of the object to look up dependencies. /// Name of the object to look up dependencies. /// public CalcDependencyCollection DependenciesReferenceTo(CalcDependencyObjectType referencedObjectType, string referencedObjectName, string referencedTableName) { CalcDependencyCollection returnVal = new CalcDependencyCollection(); LookUpDependenciesReferenceTo(referencedObjectType, referencedObjectName, referencedTableName, returnVal); return returnVal; } private void LookUpDependenciesReferenceTo(CalcDependencyObjectType referencedObjectType, string referencedObjectName, string referencedTableName, CalcDependencyCollection returnVal) { foreach (CalcDependency calcDependency in this) { if ( (calcDependency.ReferencedObjectType == referencedObjectType && referencedObjectType != CalcDependencyObjectType.Partition && calcDependency.ReferencedObjectName == referencedObjectName) || (calcDependency.ReferencedObjectType == referencedObjectType && referencedObjectType == CalcDependencyObjectType.Partition && calcDependency.ReferencedTableName == referencedTableName) //References to table-partition expressions are by table name, not partition name ) { LookUpDependenciesReferenceTo(calcDependency.ObjectType, calcDependency.ObjectName, calcDependency.TableName, returnVal); returnVal.Add(calcDependency); } } } /// /// Find an object in the collection by name. /// /// /// CalcDependency object if found. Null if not found. public CalcDependency FindByName(string name) { foreach (CalcDependency calcDependency in this) { if (calcDependency.ObjectName == name) { return calcDependency; } } return null; } /// /// A Boolean specifying whether the collection contains object by name. /// /// /// True if the object is found, or False if it's not found. public bool ContainsName(string name) { foreach (CalcDependency calcDependency in this) { if (calcDependency.ObjectName == name) { return true; } } return false; } /// /// Removes an object from the collection by its name. /// /// /// True if the object was removed, or False if was not found. public bool Remove(string name) { foreach (CalcDependency calcDependency in this) { if (calcDependency.ObjectName == name) { this.Remove(calcDependency); return true; } } return false; } } }