v10.10.2024.0701 使用IJsonHost改进Json序列化
大石头 编写于 2024-07-01 08:36:34 大石头 提交于 2024-07-01 08:48:33
X
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NewLife;
using NewLife.Model;
using NewLife.Reflection;
using XCode.DataAccessLayer;
using XTemplate.Templating;

namespace XCoder
{
    /// <summary>代码生成引擎</summary>
    public class Engine
    {
        #region 属性
        public const String TemplatePath = "Template";

        private static Dictionary<String, String> _Templates;
        /// <summary>模版</summary>
        public static Dictionary<String, String> Templates { get { return _Templates ?? (_Templates = Source.GetTemplates()); } }

        private static List<String> _FileTemplates;
        /// <summary>文件模版</summary>
        public static List<String> FileTemplates
        {
            get
            {
                if (_FileTemplates == null)
                {
                    var list = new List<string>();

                    var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TemplatePath);
                    if (Directory.Exists(dir))
                    {
                        var ds = Directory.GetDirectories(dir);
                        if (ds != null && ds.Length > 0)
                        {
                            foreach (var item in ds)
                            {
                                var di = new DirectoryInfo(item);
                                list.Add(di.Name);
                            }
                        }
                    }
                    _FileTemplates = list.OrderBy(e => e).ToList();
                }
                return _FileTemplates;
            }
        }

        public Engine(ModelConfig config)
        {
            Config = config;
        }

        private ModelConfig _Config;
        /// <summary>配置</summary>
        public ModelConfig Config { get { return _Config; } set { _Config = value; } }

        //private String _LastTableKey;
        //private List<IDataTable> _LastTables;
        private List<IDataTable> _Tables;
        /// <summary>所有表</summary>
        public List<IDataTable> Tables
        {
            get
            {
                //if (!Config.NeedFix) return _Tables;

                //// 不同的前缀、大小写选项,得到的表集合是不一样的。这里用字典来缓存
                //var key = String.Format("{0}_{1}_{2}_{3}_{4}", Config.AutoCutPrefix, Config.AutoCutTableName, Config.AutoFixWord, Config.Prefix, Config.UseID);
                ////return _cache.GetItem(key, k => FixTable(_Tables));
                //if (_LastTableKey != key)
                //{
                //    _LastTables = FixTable(_Tables);
                //    _LastTableKey = key;
                //}
                //return _LastTables;
                return _Tables;
            }
            set { _Tables = value; }
        }

        //private static ITranslate _Translate;
        ///// <summary>翻译接口</summary>
        //static ITranslate Translate { get { return _Translate ?? (_Translate = new NnhyServiceTranslate()); } }
        #endregion

        #region 构造
        static Engine()
        {
            Template.BaseClassName = typeof(XCoderBase).FullName;
        }
        #endregion

        #region 生成
        /// <summary>生成代码,参数由Config传入</summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public String[] Render(String tableName)
        {
            var tables = Tables;
            if (tables == null || tables.Count < 1) return null;

            //var table = tables.Find(e => e.Name.EqualIC(tableName) || e.TableName.EqualIC(tableName));
            var table = tables.Find(e => tableName.EqualIgnoreCase(e.Name, e.TableName));
            if (table == null) return null;

            return Render(table);
        }

        public String[] Render(IDataTable table)
        {
            var data = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            //data["Config"] = Config;
            data["Tables"] = Tables;
            data["Table"] = table;

            #region 配置
            // 复制表属性到配置
            var cfg = new ModelConfig();
            foreach (var pi in cfg.GetType().GetProperties(true))
            {
                if (table.Properties.ContainsKey(pi.Name))
                    cfg.SetValue(pi, table.Properties[pi.Name]);
                else
                    cfg.SetValue(pi, Config.GetValue(pi));
            }

            #region 命名空间处理
            var NameSpace = cfg.NameSpace;
            var reg = new Regex(@"\$\((\w+)\)", RegexOptions.Compiled);
            NameSpace = reg.Replace(NameSpace, math =>
            {
                var key = math.Groups[1].Value;
                if (String.IsNullOrEmpty(key)) return null;

                var pix = typeof(IDataTable).GetPropertyEx(key);
                if (pix != null)
                    return (String)table.GetValue(pix);
                else
                    return table.Properties[key];
            });
            NewLife.Log.XTrace.WriteLine("NameSpace" + Config.NameSpace + "@" + NameSpace);
            cfg.NameSpace = NameSpace;
            #endregion

            data["Config"] = cfg;
            #endregion

            #region 模版预处理
            // 声明模版引擎
            //Template tt = new Template();
            Template.Debug = Config.Debug;
            var templates = new Dictionary<string, string>();
            // 每一个模版的编码,用于作为输出文件的编码
            var encs = new List<Encoding>();

            var tempName = Config.TemplateName;
            var tempKind = "";
            var p = tempName.IndexOf(']');
            if (p >= 0)
            {
                tempKind = tempName.Substring(0, p + 1);
                tempName = tempName.Substring(p + 1);
            }
            if (tempKind == "[内置]")
            {
                // 系统模版
                foreach (var item in Templates)
                {
                    var key = item.Key;
                    var name = key.Substring(0, key.IndexOf("."));
                    if (name != tempName) continue;

                    var content = item.Value;

                    // 添加文件头
                    if (Config.UseHeadTemplate && !String.IsNullOrEmpty(Config.HeadTemplate) && key.EndsWithIgnoreCase(".cs"))
                        content = Config.HeadTemplate + content;

                    templates.Add(key.Substring(name.Length + 1), content);
                    encs.Add(Encoding.UTF8);
                }
            }
            else
            {
                // 文件模版
                //var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TemplatePath);
                //dir = Path.Combine(dir, tempName);
                var dir = TemplatePath.GetFullPath().CombinePath(tempName);
                if (!Directory.Exists(dir)) throw new XException("找不到模版目录{0},没有可用模版!", dir);

                var ss = Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly);
                if (ss != null && ss.Length > 0)
                {
                    foreach (var item in ss)
                    {
                        if (item.EndsWithIgnoreCase("scc")) continue;

                        var content = File.ReadAllText(item);

                        var name = item.Substring(dir.Length);
                        if (name.StartsWith(@"\")) name = name.Substring(1);

                        // 添加文件头
                        if (Config.UseHeadTemplate && !String.IsNullOrEmpty(Config.HeadTemplate) && name.EndsWithIgnoreCase(".cs"))
                            content = Config.HeadTemplate + content;

                        templates.Add(name, content);
                        encs.Add(GetEncoding(item));
                    }
                }
            }
            if (templates.Count < 1) throw new Exception("没有可用模版!");

            var tt = Template.Create(templates);
            if (tempName.StartsWith("*")) tempName = tempName.Substring(1);
            tt.AssemblyName = tempName;
            #endregion
            
            #region 输出目录预处理
            var outpath = Config.OutputPath;
            // 使用正则替换处理 命名空间处已经定义
            //var reg = new Regex(@"\$\((\w+)\)", RegexOptions.Compiled);
            outpath = reg.Replace(outpath, math =>
            {
                var key = math.Groups[1].Value;
                if (String.IsNullOrEmpty(key)) return null;

                var pix = typeof(IDataTable).GetPropertyEx(key);
                if (pix != null)
                    return (String)table.GetValue(pix);
                else
                    return table.Properties[key];
            });
            #endregion

            #region 编译生成
            // 编译模版。这里至少要处理,只有经过了处理,才知道模版项是不是被包含的
            tt.Compile();

            var rs = new List<String>();
            var i = -1;
            foreach (var item in tt.Templates)
            {
                i++;
                if (item.Included) continue;

                // 计算输出文件名
                var fileName = Path.GetFileName(item.Name);
                var fname = Config.UseCNFileName ? table.DisplayName : table.Name;
                fname = fname.Replace("/", "_").Replace("\\", "_").Replace("?", null);
                // 如果中文名无效,采用英文名
                if (String.IsNullOrEmpty(Path.GetFileNameWithoutExtension(fname)) || fname[0] == '.') fname = table.Name;
                fileName = fileName.Replace("类名", fname).Replace("中文名", fname).Replace("连接名", Config.EntityConnName);

                fileName = Path.Combine(outpath, fileName);

                // 如果不覆盖,并且目标文件已存在,则跳过
                if (!Config.Override && File.Exists(fileName)) continue;

                var content = tt.Render(item.Name, data);

                var dir = Path.GetDirectoryName(fileName);
                if (!String.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir);
                //File.WriteAllText(fileName, content, Encoding.UTF8);
                // 将文件保存为utf-8无bom格式
                //File.WriteAllText(fileName, content, new UTF8Encoding(false));

                // aspx页面如果不是UTF8编码,很有可能出现页面中文乱码,CMX生成的页面文件出现该情况
                // 使用模版文件本身的文件编码来作为输出文件的编码,默认UTF8
                File.WriteAllText(fileName, content, encs[i]);

                rs.Add(content);
            }
            return rs.ToArray();
            #endregion
        }
        #endregion

        #region 修正表
        ///// <summary>预先修正表名等各种东西,简化模版编写。</summary>
        //public List<IDataTable> FixTable(List<IDataTable> tables)
        //{
        //    if (tables == null || tables.Count < 1) return tables;

        //    var type = tables[0].GetType();
        //    var list = tables.Select(dt => (type.CreateInstance() as IDataTable).CopyAllFrom(dt)).ToList();

        //    var noCNDic = new Dictionary<object, string>();
        //    var existTrans = new List<string>();

        //    var mr = ObjectContainer.Current.Resolve<IModelResolver>();
        //    //mr.AutoCutPrefix = Config.AutoCutPrefix;
        //    //mr.AutoCutTableName = Config.AutoCutTableName;
        //    //mr.AutoFixWord = Config.AutoFixWord;
        //    //mr.FilterPrefixs = ("" + Config.Prefix).Split(',', ';');
        //    //mr.UseID = Config.UseID;

        //    #region 修正数据
        //    //foreach (var table in list)
        //    //{
        //    //    table.Name = mr.GetName(table.TableName);

        //    //    if (String.IsNullOrEmpty(table.Description))
        //    //        noCNDic.Add(table, table.Name);
        //    //    //else
        //    //    //    AddExistTranslate(existTrans, !string.IsNullOrEmpty(table.Name) ? table.Name : table.TableName, table.Description);

        //    //    // 字段
        //    //    foreach (var dc in table.Columns)
        //    //    {
        //    //        dc.Name = mr.GetName(dc);

        //    //        if (String.IsNullOrEmpty(dc.Description))
        //    //            noCNDic.Add(dc, dc.Name);
        //    //        //else
        //    //        //    AddExistTranslate(existTrans, !string.IsNullOrEmpty(dc.Name) ? dc.Name : dc.ColumnName, dc.Description);
        //    //    }

        //    //    //table.Fix();
        //    //}

        //    ModelHelper.Connect(list);
        //    #endregion

        //    //#region 异步调用接口修正中文名
        //    ////if (Config.UseCNFileName && noCNDic.Count > 0)
        //    //if (noCNDic.Count > 0)
        //    //{
        //    //    ThreadPoolX.QueueUserWorkItem(TranslateWords, noCNDic);
        //    //}
        //    //#endregion

        //    //#region 提交已翻译的项目
        //    //if (existTrans.Count > 0)
        //    //{
        //    //    ThreadPoolX.QueueUserWorkItem(SubmitTranslateNew, existTrans.ToArray());
        //    //}
        //    //#endregion

        //    return list;
        //}

        //void TranslateWords(Object state)
        //{
        //    var dic = state as Dictionary<Object, String>;

        //    var words = new string[dic.Values.Count];
        //    dic.Values.CopyTo(words, 0);
        //    var rs = Translate.Translate(words);
        //    if (rs == null || rs.Length < 1) return;

        //    var ts = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        //    for (int i = 0; i < words.Length && i < rs.Length; i++)
        //    {
        //        var key = words[i].Replace(" ", null);
        //        if (!ts.ContainsKey(key) && !String.IsNullOrEmpty(rs[i]) && words[i] != rs[i] && key != rs[i].Replace(" ", null)) ts.Add(key, rs[i].Replace(" ", null));
        //    }

        //    foreach (var item in dic)
        //    {
        //        if (!ts.ContainsKey(item.Value) || String.IsNullOrEmpty(ts[item.Value])) continue;

        //        if (item.Key is IDataTable)
        //            (item.Key as IDataTable).Description = ts[item.Value];
        //        else if (item.Key is IDataColumn)
        //            (item.Key as IDataColumn).Description = ts[item.Value];
        //    }
        //}

        //void SubmitTranslateNew(object state)
        //{
        //    var existTrans = state as string[];
        //    if (existTrans != null && existTrans.Length > 0)
        //    {
        //        //var serv = new NnhyServiceTranslate();
        //        Translate.TranslateNew("1", existTrans);
        //        var trans = new List<string>(ExistSubmitTrans);
        //        trans.AddRange(existTrans);
        //        ExistSubmitTrans = trans.ToArray();
        //    }
        //}

        //private static string[] _ExistSubmitTrans;
        //private static string[] ExistSubmitTrans
        //{
        //    get
        //    {
        //        if (_ExistSubmitTrans == null)
        //        {
        //            var f = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "XCoder");
        //            f = Path.Combine(f, "SubmitedTranslations.dat");
        //            if (File.Exists(f))
        //                _ExistSubmitTrans = File.ReadAllLines(f);
        //            else
        //                _ExistSubmitTrans = new string[] { };
        //        }
        //        return _ExistSubmitTrans;
        //    }
        //    set
        //    {
        //        if (value != null && value.Length > 0)
        //        {
        //            var f = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "XCoder");
        //            if (!String.IsNullOrEmpty(f) && !Directory.Exists(f)) Directory.CreateDirectory(f);
        //            f = Path.Combine(f, "SubmitedTranslations.dat");
        //            File.WriteAllLines(f, value);
        //            _ExistSubmitTrans = value;
        //        }
        //    }
        //}

        //void AddExistTranslate(List<string> trans, string text, string tranText)
        //{
        //    if (text != null) text = text.Trim();
        //    if (tranText != null) tranText = tranText.Trim();
        //    if (string.IsNullOrEmpty(text)) return;
        //    if (string.IsNullOrEmpty(tranText)) return;
        //    if (text.EqualIgnoreCase(tranText)) return;

        //    for (int i = 0; i < trans.Count - 1; i += 2)
        //    {
        //        if (trans[i].EqualIgnoreCase(text) && trans[i + 1].EqualIgnoreCase(tranText)) return;
        //    }

        //    var ests = ExistSubmitTrans;
        //    for (int i = 0; i < ests.Length - 1; i += 2)
        //    {
        //        if (ests[i].EqualIgnoreCase(text) && ests[i + 1].EqualIgnoreCase(tranText)) return;
        //    }

        //    trans.Add(text);
        //    trans.Add(tranText);
        //}
        #endregion

        #region 辅助
        /// <summary>获取文件编码</summary>
        /// <param name="file"></param>
        /// <returns></returns>
        static Encoding GetEncoding(String file)
        {
            if (String.IsNullOrEmpty(file) || !File.Exists(file)) return Encoding.UTF8;

            using (var reader = new StreamReader(file, Encoding.UTF8, true))
            {
                return reader.CurrentEncoding;
            }
        }
        #endregion
    }
}