aardio 文档

aardio 范例: .NET 命名空间分析工具(Microsoft Learn HTML 版)

//.NET 命名空间分析工具(Microsoft Learn HTML 版)
import win.ui;
/*DSG{{*/
var winform = win.form(text=".NET Framework 命名空间分析工具";right=830;bottom=469)
winform.add(
button={cls="button";text="分析";left=673;top=7;right=820;bottom=43;anchors={top=1;right=1};z=3};
edit={cls="edit";left=15;top=54;right=816;bottom=456;anchors={left=1;top=1;right=1;bottom=1};edge=1;hscroll=1;multiline=1;vscroll=1;z=1};
editNameSpace={cls="edit";text="System.Reflection";left=177;top=12;right=546;bottom=41;anchors={top=1;right=1;bottom=1};edge=1;multiline=1;z=2};
editNetVersion={cls="edit";text="4.0";left=599;top=12;right=654;bottom=41;edge=1;z=5};
static={cls="static";text=".NET Framework 命名空间:";left=12;top=10;right=169;bottom=35;align="right";center=1;anchors={left=1;top=1};transparent=1;z=4};
static2={cls="static";text="版本:";left=549;top=10;right=597;bottom=35;align="right";center=1;transparent=1;z=6}
)
/*}}*/

winform.edit.limit = -1;

winform.button.oncommand = function(id,event){
    var netNamespace = string.trim(winform.editNameSpace.text);
    var netVersion = string.trim(winform.editNetVersion.text);
    if(!#netNamespace){
        return winform.editNameSpace.showErrorTip("请输入 .NET 命名空间")
    }
    if(!#netVersion){
        return winform.editNetVersion.showErrorTip("请输入 .NET Framework 版本,例如 3.5、4.0、4.8")
    }

    winform.edit.text = "";

    thread.invoke(
        function(winform,netNamespace,netVersion){
            winform.button.disabledText = {"✶";"✸";"✹";"✺";"✹";"✷"};

            var finish = function(message,isError){
                winform.button.disabledText = null;
                if(message){
                    if(isError) winform.msgboxErr(message);
                    else winform.edit.print(message);
                }
            }

            var normalizeView = function(netVersion){
                netVersion = string.trim(netVersion);
                if(string.match(netVersion,"^netframework\-")) return netVersion;
                return "netframework-" ++ netVersion;
            }

            var hasMoniker = function(monikers,view){
                if(!monikers) return false;
                return string.indexOf(" " ++ monikers ++ " "," " ++ view ++ " ") != null;
            }

            var makeApiUrl = function(href){
                if(!href) return null;
                if(string.match(href,"^https?\://")) return href;
                if(string.left(href,1) == "/") return "https://learn.microsoft.com" ++ href;
                return inet.url.canonicalize("https://learn.microsoft.com/en-us/dotnet/api/" ++ href);
            }

            var getAssembly = function(htmlClient,typeUrl,view){
                var htmlDoc,err = htmlClient.get(typeUrl);
                if(!htmlDoc) return null,err || "抓取类型页失败";

                for ele in htmlDoc.eachQuery(tagName="dl"){
                    if(!hasMoniker(ele["data-moniker"],view)) continue;

                    var text = string.trim(ele.innerText());
                    if(!string.match(text,"^Assembly\:")) continue;

                    var assembly;
                    if(ele.dd[1]) assembly = string.trim(ele.dd[1].innerText());
                    if(!assembly) assembly = string.match(text,"^Assembly\:\s*([^\r\n]+\.dll)");

                    if(assembly && string.endsWith(assembly,".dll")){
                        return assembly;
                    }
                }
                return null,"未在类型页找到匹配 " ++ view ++ " 的 Assembly 信息";
            }

            import inet.url;
            import web.rest.htmlClient;
            var htmlClient = web.rest.htmlClient();
            var view = normalizeView(netVersion);
            var namespaceUrl = "https://learn.microsoft.com/en-us/dotnet/api/" 
                ++ string.lower(netNamespace) 
                ++ "?view=" 
                ++ view;

            winform.edit.print("正在获取命名空间数据:",netNamespace);
            var namespaceDoc,err = htmlClient.get(namespaceUrl);
            if(!namespaceDoc){
                finish("获取命名空间数据失败:" ++ (err || namespaceUrl),true);
                return;
            }

            var items = [];
            var seen = {};
            for tr in namespaceDoc.eachQuery(tagName="tr"){
                if(!hasMoniker(tr["data-moniker"],view)) continue;

                var a;
                for ele in tr.eachQuery(tagName="a"){
                    if(ele.class && string.indexOf(ele.class,"xref")){
                        a = ele;
                        break;
                    }
                }

                if(a){
                    var typeName = string.trim(a.innerText());
                    var href = a.href;
                    if(!#typeName || !href) continue;

                    var apiName = netNamespace ++ "." ++ typeName;
                    if(seen[apiName]) continue;
                    seen[apiName] = true;

                    table.push(items,{
                        apiName = apiName;
                        href = href;
                        url = makeApiUrl(href);
                    });
                }
            }

            if(!#items){
                finish("未找到属于 " ++ view ++ ' 的类型列表。\r\n' ++ namespaceUrl,true);
                return;
            }

            winform.edit.print("找到类型数量:",#items,",开始逐个抓取类型页分析 Assembly ...");

            var map = {};
            var failed = [];
            for i,item in items{
                var assembly,err = getAssembly(htmlClient,item.url,view);
                if(assembly){
                    if(!map[assembly]) map[assembly] = [];
                    table.push(map[assembly],item.apiName);
                    winform.edit.updateLastLine(i,"/",#items,"  ",item.apiName,"  =>  ",assembly);
                }
                else {
                    table.push(failed,{apiName=item.apiName;url=item.url;err=err});
                    winform.edit.updateLastLine(i,"/",#items,"  ",item.apiName,"  =>  跳过:",err);
                }
            }

            import JSON;
            winform.edit.text = JSON.stringify(map,true);
            if(#failed){
                winform.edit.print('\r\n/* 未能识别 Assembly 的项目:\r\n',JSON.stringify(failed,true),'\r\n*/');
            }
            winform.button.disabledText = null;
        },winform,netNamespace,netVersion
    )
}

winform.edit.text = /*****************
// aardio 导入 .NET 命名空间示例
import dotNet;

// 这里只是举例,System 已随 dotNet 库默认导入,不必要再重复导入。
// 存在同名标准库库的可用 import 语句导入,例如 import System;
// 访问已导入 .NET 命名空间的下级名字空间,在同一程序集内则按需自动导入。
dotNet.import("System"); 

// 下级命名空间不在同一程序集,但程序集在之前已加载的,aardio 也会自动导入。
// 不在同一程序集内,且所属程序集未加载的,必须用哪个导入哪个,不会覆盖已存在的父命名空间。
// 这里只是举例,mscorlib 已随 dotNet 库默认加载,通常不必重复导入。
dotNet.import("System.Security.Cryptography","mscorlib");

// 不会自动导入或替换上级命名空间,例如下面的代码不能替代 dotNet.import("System"); 
dotNet.import("System.Security.Cryptography.X509Certificates","System"); 

// .NET Framework 自带的命名空间在不同的 DLL 实现。
// 所以我们需要用这个工具,分析一个命名空间在哪个 DLL 里的成员最多。
// 就用该 DLL 导入该命名空间,可以少写 dotNet.import 语句。
*****************/

winform.show();
win.loopMessage();
Markdown 格式