Пример #1
0
        /// <summary>
        /// Specific to Graph-Viz (ver 2.38+) - produces the .gv file 
        /// to be compiled by dot.exe resulting in a class diagram of 
        /// the <see cref="assembly"/>
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="typeFullName"></param>
        /// <returns></returns>
        public static string GetClassDiagram(Assembly assembly, string typeFullName)
        {
            if(assembly == null)
                throw new ArgumentNullException(nameof(assembly));
            if(string.IsNullOrWhiteSpace(typeFullName))
                throw new ArgumentNullException(nameof(typeFullName));
            var asmType = assembly.NfGetType(typeFullName);
            if (asmType == null)
                throw new RahRowRagee(
                    $"This type '{typeFullName}' could not be found in the assembly '{assembly.GetName().Name}'");

            var gv = new StringBuilder();
            var graphName = NfTypeName.SafeDotNetIdentifier(assembly.GetName().Name);

            var topClass = GetCgOfType(assembly, typeFullName, false);
            var allCgTypeNames =
                topClass.AllPropertyTypes.Where(
                    x =>
                        !string.Equals(x, typeFullName, StringComparison.OrdinalIgnoreCase) &&
                        assembly.GetType(x) != null).ToList();

            var allCgTypes =
                allCgTypeNames.Where(x => !string.Equals(x, typeFullName, StringComparison.OrdinalIgnoreCase))
                    .Select(x => GetCgOfType(assembly, x, false))
                    .ToList();

            foreach (var aCgType in allCgTypes)
            {
                aCgType.IsEnum = topClass.Properties.FirstOrDefault(x => x.TypeName == aCgType.FullName && x.IsEnum) !=
                                 null;
            }

            //keep the diagram from exploding by multiples.
            var propertyManifold =
                allCgTypes.SelectMany(x => x.AllPropertyTypes)
                    .Where(
                        x =>
                            !string.Equals(x, typeFullName, StringComparison.OrdinalIgnoreCase) &&
                            !allCgTypeNames.Contains(x))
                    .Distinct()
                    .ToList();

            //determine which types require an empty class node
            var missingTypes =
                allCgTypes.SelectMany(x => x.AllPropertyTypes).Where(x => assembly.NfGetType(x) == null).Distinct().ToList();

            missingTypes.AddRange(
                topClass.AllPropertyTypes.Where(x => assembly.NfGetType(x) == null && !missingTypes.Contains(x)));

            gv.Append($"digraph {graphName}Assembly");
            gv.AppendLine("{");
            gv.AppendLine("graph [rankdir=\"LR\"];");
            gv.AppendLine("node [fontname=\"Consolas\"];");

            gv.AppendLine(topClass.ToGraphVizNode());

            foreach (var missing in missingTypes)
                gv.AppendLine(GraphVizExtensions.EmptyGraphVizClassNode(missing,topClass.EnumsValues(missing)));

            foreach (var far in propertyManifold)
                gv.AppendLine(GraphVizExtensions.EmptyGraphVizClassNode(far, topClass.EnumsValues(far)));

            foreach (var cg in allCgTypes.Where(x => !x.IsEnum))
                gv.AppendLine(cg.ToGraphVizNode());

            foreach (
                var cg in
                    allCgTypes.Where(
                        x => x.IsEnum && !missingTypes.Contains(x.FullName) && !propertyManifold.Contains(x.FullName)))
                gv.AppendLine(GraphVizExtensions.EmptyGraphVizClassNode(cg.FullName, topClass.EnumsValues(cg.FullName)));

            gv.AppendLine();
            gv.AppendLine();

            gv.AppendLine(topClass.ToGraphVizEdge());

            foreach (var cg in allCgTypes)
                gv.AppendLine(cg.ToGraphVizEdge());

            gv.AppendLine("}");
            return gv.ToString();
        }
Пример #2
0
        internal static List<FlattenedLine> FlattenType(Assembly assembly, string typeFullName,
            ref int currentDepth, int maxDepth, string limitOnValueType, bool displayEnums, Stack<FlattenedItem> fiValueTypes, Stack typeStack)
        {
            var printList = new List<FlattenedLine>();
            if (string.IsNullOrWhiteSpace(typeFullName))
                return printList;

            Func<PropertyInfo, string, bool> limitOnPi =
                (info, s) =>
                    string.IsNullOrWhiteSpace(s) ||
                    string.Equals($"{info.PropertyType}", s, StringComparison.OrdinalIgnoreCase) ||
                    string.Equals(NfTypeName.GetLastTypeNameFromArrayAndGeneric(info.PropertyType), s,
                        StringComparison.OrdinalIgnoreCase) ||
                    (s == Constants.ENUM && NfTypeName.IsEnumType(info.PropertyType));

            var currentType = assembly.NfGetType(typeFullName);
            if (currentType == null)
                return printList;

            //top frame of recursive calls will perform this
            if (fiValueTypes == null)
            {
                if (maxDepth <= 0)
                    maxDepth = 16;
                typeStack = new Stack();
                typeStack.Push(typeFullName);
                fiValueTypes = new Stack<FlattenedItem>();
                fiValueTypes.Push(new FlattenedItem(currentType)
                {
                    FlName = NfTypeName.GetTypeNameWithoutNamespace(typeFullName)
                });
            }

            var typeNamesList =
                currentType.GetProperties(NfConfig.DefaultFlags)
                    .Where(
                        x =>
                            (NfTypeName.IsValueTypeProperty(x) && limitOnPi(x, limitOnValueType)
                             || (limitOnValueType == Constants.ENUM && limitOnPi(x, limitOnValueType)))
                    //more limbo branching for enums
                    )
                    .Select(p => new Tuple<Type, string>(p.PropertyType, p.Name))
                    .ToList();

            foreach (var typeNamePair in typeNamesList)
            {
                var pVtype = typeNamePair.Item1;
                var pVname = typeNamePair.Item2;

                fiValueTypes.Push(new FlattenedItem(pVtype) { FlName = pVname });
                var fiItems = fiValueTypes.ToList();
                fiItems.Reverse();
                printList.Add(new FlattenedLine(fiItems.Distinct(new FlattenedItemComparer()).ToList())
                {
                    ValueType = $"{typeNamePair.Item1}"
                });
                fiValueTypes.Pop();
            }

            //then recurse the object types
            foreach (
                var p in
                    currentType.GetProperties(NfConfig.DefaultFlags)
                        .Where(x => !NfTypeName.IsValueTypeProperty(x)))
            {
                var typeIn = NfTypeName.GetLastTypeNameFromArrayAndGeneric(p.PropertyType);

                if (typeIn == null || typeStack.Contains(typeIn)) continue;

                var fi = new FlattenedItem(p.PropertyType) {FlName = p.Name};
                if (fiValueTypes.ToList().Any(x => x.FlType == p.PropertyType))
                    continue;

                fiValueTypes.Push(fi);
                typeStack.Push(typeIn);
                currentDepth += 1;

                //time to go
                if (currentDepth >= maxDepth)
                    return printList;

                //enum types being handled as limbo between value type and ref type
                string[] enumVals;
                if (displayEnums && NfTypeName.IsEnumType(p.PropertyType, out enumVals))
                {
                    foreach (var ev in enumVals)
                    {
                        fiValueTypes.Push(new FlattenedItem(typeof(Enum)) {FlName = ev});
                        var fiItems = fiValueTypes.ToList();
                        fiItems.Reverse();
                        printList.Add(new FlattenedLine(fiItems.Distinct(new FlattenedItemComparer()).ToList())
                        {
                            ValueType = String.Empty
                        });
                        fiValueTypes.Pop();
                    }
                }
                else
                {
                    printList.AddRange(FlattenType(assembly, fi.TypeFullName, ref currentDepth, maxDepth,
                        limitOnValueType, displayEnums, fiValueTypes, typeStack));
                }

                fiValueTypes.Pop();
                typeStack.Pop();
                currentDepth -= 1;
            }
            return printList;
        }
Пример #3
0
        /// <summary>
        /// Get a custom type specific for Code Gen purposes based on the 
        /// given <see cref="typeFullName"/>
        /// </summary>
        /// <param name="assembly">A reference to the assembly in which the type is located.</param>
        /// <param name="typeFullName">The type which will produce the cgObject.</param>
        /// <param name="valueTypeOnly">Will only export Fields and Properties whose base type extends System.ValueType</param>
        /// <param name="resolveDependencies">Switch to have the IL of the type parsed and all dependent calls Metadata tokens added.</param>
        /// <returns></returns>
        public static CgType GetCgOfType(Assembly assembly, string typeFullName, 
            bool valueTypeOnly, bool resolveDependencies = false)
        {
            if (assembly == null || string.IsNullOrWhiteSpace(typeFullName))
                return null;

            var asmType = assembly.NfGetType(typeFullName);
            var cgType = GetCgOfType(asmType, valueTypeOnly,resolveDependencies);
            return cgType;
        }
Пример #4
0
        /// <summary>
        /// Intended for resolving method calls of types defined in another assembly.
        /// </summary>
        /// <param name="owningAsm"></param>
        /// <param name="tokenName">The name as drafted from <see cref="AssemblyAnalysis.ConvertToMetadataTokenName"/></param>
        /// <param name="mi"></param>
        /// <param name="msgOut">Inteneded for debug trace from the Console.</param>
        /// <returns></returns>
        internal bool TryResolveRtMemberInfo(Assembly owningAsm, string tokenName, out MemberInfo mi, StringBuilder msgOut = null)
        {
            //default the out variable
            mi = null;

            //expect token name to match naming given herein
            if (String.IsNullOrWhiteSpace(tokenName))
            {
                if (msgOut != null)
                    msgOut.Append(", Message:'the token name is null'");
                return false;
            }
            if (!tokenName.Contains(Constants.TYPE_METHOD_NAME_SPLIT_ON))
            {
                if (msgOut != null)
                    msgOut.AppendFormat(", Message:'[{0}] does not contain {1}'", tokenName,
                        Constants.TYPE_METHOD_NAME_SPLIT_ON);
            }

            if (owningAsm == null)
            {
                if (msgOut != null)
                    msgOut.Append(", Message:'the owning assembly is null'");
                return false;
            }

            var assemblyName = owningAsm.GetName().Name;
            var typeName = AssemblyAnalysis.ParseTypeNameFromTokenName(tokenName, assemblyName);

            if (String.IsNullOrWhiteSpace(typeName))
            {
                if (msgOut != null)
                    msgOut.Append(", Message:'could not parse type name'");
                return false;
            }
            Type asmType = null;
            try
            {
                //framework throwing null-ref ex despite null checks
                asmType = owningAsm.NfGetType(typeName, false, _myProgram.LogFile);
            }
            catch
            {
                if (msgOut != null)
                    msgOut.Append(", Message:'Assembly.GetType threw exception'");
                return false;
            }
            if (asmType == null)
            {
                if (msgOut != null)
                    msgOut.AppendFormat(", Message:'assembly {0} could not resolve {1}'", assemblyName,
                        typeName);
                return false;
            }

            var methodName = AssemblyAnalysis.ParseMethodNameFromTokenName(tokenName);
            if (String.IsNullOrWhiteSpace(methodName))
            {
                if (msgOut != null)
                    msgOut.Append(", Message:'could not parse method name'");
                return false;
            }

            MethodInfo methodInfo = null;

            //try easiest first
            try
            {
                methodInfo = asmType.NfGetMethod(methodName, NfConfig.DefaultFlags);
            }
            catch (AmbiguousMatchException) { }//is overloaded

            //try it the formal way
            if (methodInfo == null)
            {
                var args = AssemblyAnalysis.ParseArgsFromTokenName(tokenName).ToArray();
                var argTypes = args.Length <= 0
                    ? Type.EmptyTypes
                    : args.Select(Type.GetType).Where(x => x != null).ToArray();

                //there must be a one-for-one match of string names to first-class types
                if (args.Length == argTypes.Length)
                    methodInfo = asmType.NfGetMethod(methodName, NfConfig.DefaultFlags, null, argTypes, null,
                        false, _myProgram.LogFile);
            }

            //try it the very slow but certian way
            if (methodInfo == null)
            {
                var methodInfos = asmType.NfGetMethods(NfConfig.DefaultFlags, false, _myProgram.LogFile);
                if (methodInfos.Length <= 0)
                {
                    if (msgOut != null)
                    {
                        msgOut.AppendFormat(", Assembly:'{0}'\n", assemblyName);
                        msgOut.AppendFormat(", Type:'{0}'\n", typeName);
                        msgOut.Append(", Message:'does not have any methods'");
                    }
                    return false;
                }
                foreach (var info in methodInfos)
                {
                    var asTokenName = AssemblyAnalysis.ConvertToMetadataTokenName(info, _myProgram.AsmIndicies, IsIgnore);
                    if (asTokenName == null || string.IsNullOrWhiteSpace(asTokenName.Name))
                        continue;
                    if (string.Equals(asTokenName.Name, tokenName))
                    {
                        methodInfo = info;
                        break;
                    }
                }
            }

            if (methodInfo == null)
            {
                if (msgOut != null)
                {
                    msgOut.AppendFormat(", Assembly:'{0}'\n", assemblyName);
                    msgOut.AppendFormat(", Type:'{0}'\n", typeName);
                    msgOut.AppendFormat(", Method:'{0}'\n", methodName);
                    msgOut.Append(", Message:'was not found'");
                }
                return false;
            }

            mi = methodInfo;
            return true;
        }