.NET CIL Browser - Back to table of contents

Source file: WebsiteGenerator.cs

Files in CilBrowser.Core directory:

AssemblyServer.cs

CilBrowser.Core.csproj

CilBrowserOptions.cs

FileUtils.cs

HtmlAttribute.cs

HtmlBuilder.cs

HtmlGenerator.cs

ServerBase.cs

SourceServer.cs

Utils.cs

WebsiteGenerator.cs

/* CIL Browser (https://github.com/MSDN-WhiteKnight/CilBrowser)
 * Copyright (c) 2023,  MSDN.WhiteKnight 
 * License: BSD 3-Clause */
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using CilBrowser.Core.Structure;

namespace CilBrowser.Core
{
    /// <summary>
    /// Indexes contents of assemblies or source directories and invokes <see cref="HtmlGenerator"/> to visualize
    /// them as a static website
    /// </summary>
    public static class WebsiteGenerator
    {
        /// <summary>
        /// Generates a static website that contains disassembled CIL for the specified assembly
        /// </summary>
        public static void GenerateFromAssembly(Assembly ass, string nsFilter, string outputPath, string customFooter)
        {
            HtmlGenerator generator = new HtmlGenerator(ass, nsFilter, customFooter);
            Directory.CreateDirectory(outputPath);

            //write assembly manifest
            string html = generator.VisualizeAssemblyManifest(ass);
            File.WriteAllText(Path.Combine(outputPath, "assembly.html"), html, Encoding.UTF8);

            //create Table of contents builder
            StringBuilder sb = new StringBuilder(1000);
            HtmlBuilder toc = new HtmlBuilder(sb);
            AssemblyName an = ass.GetName();
            Console.WriteLine("Generating website for " + an.Name);

            if (!string.IsNullOrEmpty(nsFilter)) Console.WriteLine("Namespace filter: " + nsFilter);

            Console.WriteLine("Output path: " + outputPath);
            HtmlGenerator.WriteTocStart(toc, ass);

            //write types
            Type[] types = ass.GetTypes();
            Dictionary<string, List<Type>> typeMap = Utils.GroupByNamespace(types);
            string[] namespaces = typeMap.Keys.ToArray();
            Array.Sort(namespaces);

            for (int i = 0; i < namespaces.Length; i++)
            {
                string nsText = namespaces[i];

                if (!string.IsNullOrEmpty(nsFilter))
                {
                    if (!Utils.StrEquals(nsText, nsFilter)) continue;
                }

                List<Type> nsTypes = typeMap[namespaces[i]];

                if (nsTypes.Count == 0) continue;

                if (nsTypes.Count == 1)
                {
                    BindingFlags all = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
                        BindingFlags.NonPublic;

                    if (Utils.StrEquals(nsTypes[0].FullName, "<Module>") &&
                        nsTypes[0].GetFields(all).Length == 0 && nsTypes[0].GetMethods(all).Length == 0)
                    {
                        continue; //ignore namespace consisting only from empty <Module> type
                    }
                }

                if (string.IsNullOrEmpty(nsText)) nsText = "(No namespace)";
                else nsText = nsText + " namespace";

                toc.WriteTag("h2", nsText);

                for (int j = 0; j < nsTypes.Count; j++)
                {
                    //file content
                    try
                    {
                        html = generator.VisualizeType(nsTypes[j], typeMap);
                    }
                    catch (NotSupportedException ex)
                    {
                        html = HtmlGenerator.VisualizeException(ex);
                        Console.WriteLine("Failed to generate HTML for " + nsTypes[j].FullName);
                        Console.WriteLine(ex.ToString());
                    }

                    string fname = HtmlGenerator.GenerateTypeFileName(nsTypes[j]);

                    if (!string.IsNullOrWhiteSpace(html))
                    {
                        Console.WriteLine(nsTypes[j].FullName);

                        //TOC entry
                        toc.StartParagraph();
                        toc.WriteHyperlink(fname, nsTypes[j].FullName);
                        toc.EndParagraph();
                    }
                    else
                    {
                        Console.WriteLine(nsTypes[j].FullName + " - empty");
                    }

                    File.WriteAllText(Path.Combine(outputPath, fname), html, Encoding.UTF8);
                }
            }

            //write TOC
            generator.WriteFooter(toc);
            toc.EndDocument();
            File.WriteAllText(Path.Combine(outputPath, "index.html"), sb.ToString(), Encoding.UTF8);
        }

        internal static string VisualizeNavigationPanel(string filename, string dirName, string[] dirFiles, 
            HashSet<string> sourceExtensions)
        {
            StringBuilder sb = new StringBuilder(1000);
            HtmlBuilder html = new HtmlBuilder(sb);
            html.WriteParagraph("Files in " + dirName + " directory:");

            //list of files
            for (int i = 0; i < dirFiles.Length; i++)
            {
                if (!FileUtils.IsSourceFile(dirFiles[i], sourceExtensions)) continue;

                html.StartParagraph();
                string currFileName = Path.GetFileName(dirFiles[i]);

                if (Utils.StrEquals(currFileName, filename))
                {
                    html.WriteTag("b", filename);
                }
                else
                {
                    string pageName = FileUtils.FileNameToPageName(currFileName);
                    html.WriteHyperlink(WebUtility.UrlEncode(pageName), currFileName);
                }

                html.EndParagraph();
            }

            return sb.ToString();
        }

        internal static string VisualizeNavigationPanel(PageNode fileNode, string dirName, PageNode[] dirFiles, TreeNodeKind kind)
        {
            StringBuilder sb = new StringBuilder(1000);
            HtmlBuilder html = new HtmlBuilder(sb);

            if (kind == TreeNodeKind.Directory) html.WriteParagraph("Files in " + dirName + " directory:");
            else html.WriteParagraph("Pages in " + dirName + ":");

            //list of pages
            for (int i = 0; i < dirFiles.Length; i++)
            {
                html.StartParagraph();
                string currFileName = dirFiles[i].Name;

                if (Utils.StrEquals(currFileName, fileNode.Name))
                {
                    html.WriteTag("b", fileNode.DisplayName);
                }
                else
                {
                    string pageName = FileUtils.FileNameToPageName(currFileName);
                    html.WriteHyperlink(WebUtility.UrlEncode(pageName), dirFiles[i].DisplayName);
                }

                html.EndParagraph();
            }

            return sb.ToString();
        }

        internal static string RenderNavigationPanel(string filepath, string filename, HashSet<string> sourceExtensions)
        {
            string dirpath = Path.GetDirectoryName(filepath);
            string dirname = Utils.GetDirectoryNameFromPath(dirpath);
            string[] files = Directory.GetFiles(dirpath);

            if (files.Length > 1) return VisualizeNavigationPanel(filename, dirname, files, sourceExtensions);
            else return string.Empty;
        }

        internal static string GetImagesURL(int level)
        {
            StringBuilder sb = new StringBuilder(level * 3);

            for (int i = 0; i < level; i++) sb.Append("../");

            sb.Append("img/");
            return sb.ToString();
        }

        internal static void RenderDirsList(string[] dirs, string dirIconURL, HtmlBuilder toc)
        {
            toc.WriteTagStart("table", HtmlBuilder.OneAttribute("cellpadding", "2px"));

            for (int i = 0; i < dirs.Length; i++)
            {
                string name = Utils.GetDirectoryNameFromPath(dirs[i]);

                if (FileUtils.IsDirectoryExcluded(name)) continue;

                //TOC entry
                toc.WriteTagStart("tr");
                toc.WriteTagStart("td");
                toc.WriteTag("img", string.Empty, HtmlBuilder.OneAttribute("src", dirIconURL));
                toc.WriteTagEnd("td");
                toc.WriteTagStart("td");
                toc.WriteHyperlink("./" + WebUtility.UrlEncode(name) + "/index.html", name);
                toc.WriteTagEnd("td");
                toc.WriteTagEnd("tr");
            }

            toc.WriteTagEnd("table");
        }

        internal static void RenderDirsList(DirectoryNode[] dirs, string dirIconURL, HtmlBuilder toc)
        {
            toc.WriteTagStart("table", HtmlBuilder.OneAttribute("cellpadding", "2px"));

            for (int i = 0; i < dirs.Length; i++)
            {
                if (dirs[i].PagesCount + dirs[i].DirectoriesCount == 0)
                {
                    continue; //not interested in empty directories
                }

                string name = dirs[i].Name;
                
                //TOC entry
                toc.WriteTagStart("tr");
                toc.WriteTagStart("td");
                toc.WriteTag("img", string.Empty, HtmlBuilder.OneAttribute("src", dirIconURL));
                toc.WriteTagEnd("td");
                toc.WriteTagStart("td");
                toc.WriteHyperlink("./" + WebUtility.UrlEncode(name) + "/index.html", name);
                toc.WriteTagEnd("td");
                toc.WriteTagEnd("tr");
            }

            toc.WriteTagEnd("table");
        }

        internal static void RenderTocEntry(string displayName, string pageName, string fileIconURL, HtmlBuilder toc)
        {
            toc.WriteTagStart("tr");
            toc.WriteTagStart("td");
            toc.WriteTag("img", string.Empty, HtmlBuilder.OneAttribute("src", fileIconURL));
            toc.WriteTagEnd("td");
            toc.WriteTagStart("td");
            toc.WriteHyperlink(WebUtility.UrlEncode(pageName), displayName);
            toc.WriteTagEnd("td");
            toc.WriteTagEnd("tr");
        }

        /// <summary>
        /// Generates a static website for the source code in the specified directory
        /// </summary>
        public static void GenerateFromSources(string sourcesPath, string outputPath, CilBrowserOptions options,
            string customFooter)
        {
            //generate HTML files
            DirectoryNode root = SourceIndexer.SourceDirectoryToTree(sourcesPath, options);
            GenerateFromTreeImpl(root, outputPath, options, customFooter, 0);

            //write icons
            Directory.CreateDirectory(Path.Combine(outputPath, "img"));
            Assembly ass = typeof(WebsiteGenerator).Assembly;
            byte[] imgContent = FileUtils.ReadFromResource(ass, "CilBrowser.Core.Images", "dir.png");
            File.WriteAllBytes(Path.Combine(outputPath, "img/dir.png"), imgContent);
            imgContent = FileUtils.ReadFromResource(ass, "CilBrowser.Core.Images", "file.png");
            File.WriteAllBytes(Path.Combine(outputPath, "img/file.png"), imgContent);
        }

        static void GenerateFromTreeImpl(DirectoryNode currentNode, string outputPath, CilBrowserOptions options,
            string customFooter, int level)
        {
            if (level > 50)
            {
                Console.WriteLine("Error: Website structure tree is too deep!");
                return;
            }

            if (currentNode.PagesCount + currentNode.DirectoriesCount == 0)
            {
                return; //this directory does not have anything interesting
            }

            Console.WriteLine("Generating website from source directory tree: " + currentNode.Name);
            Console.WriteLine("Output path: " + outputPath);
            HtmlGenerator generator = new HtmlGenerator();
            generator.CustomFooter = customFooter;
            Directory.CreateDirectory(outputPath);

            //files
            foreach (FileNode fileNode in currentNode.Pages)
            {
                Console.WriteLine(fileNode.Name);
                string pageName = FileUtils.FileNameToPageName(fileNode.Name);
                string html = fileNode.RenderToString(generator, options);
                File.WriteAllText(Path.Combine(outputPath, pageName), html, Encoding.UTF8);
            }

            //subdirectories
            foreach(DirectoryNode dir in currentNode.Directories)
            {
                string name = dir.Name;
                string urlNew;

                if (!string.IsNullOrEmpty(options.SourceControlURL)) urlNew = Utils.UrlAppend(options.SourceControlURL, name);
                else urlNew = string.Empty;

                CilBrowserOptions optionsNew = options.Copy();
                optionsNew.SourceControlURL = urlNew;
                GenerateFromTreeImpl(dir, Path.Combine(outputPath, name), optionsNew, customFooter, level + 1);
            }

            //TOC for current directory
            string tocHTML = currentNode.RenderToString(generator, options);
            File.WriteAllText(Path.Combine(outputPath, "index.html"), tocHTML, Encoding.UTF8);
        }
    }
}
View in source control

Back to table of contents


Generated by CIL Browser