/// <summary>导入某类型,导入程序集引用及命名空间引用,主要处理泛型</summary> /// <param name="item"></param> /// <param name="type"></param> void ImportType(TemplateItem item, Type type) { String name = null; try { name = type.Assembly.Location; } catch { } if (!String.IsNullOrEmpty(name) && !AssemblyReferences.Contains(name)) AssemblyReferences.Add(name); name = type.Namespace; if (!item.Imports.Contains(name)) item.Imports.Add(name); if (type.IsGenericType && !type.IsGenericTypeDefinition) { Type[] ts = type.GetGenericArguments(); foreach (Type elm in ts) { if (!elm.IsGenericParameter) ImportType(item, elm); } } }
private static String ConstructGeneratorCode(TemplateItem item, Boolean lineNumbers, String namespaceName, CodeDomProvider provider) { // 准备类名和命名空间 CodeNamespace codeNamespace = new CodeNamespace(namespaceName); // 加入引用的命名空间 foreach (String str in item.Imports) { if (!String.IsNullOrEmpty(str)) codeNamespace.Imports.Add(new CodeNamespaceImport(str)); } CodeTypeDeclaration typeDec = new CodeTypeDeclaration(item.ClassName); typeDec.IsClass = true; codeNamespace.Types.Add(typeDec); // 基类 if (!String.IsNullOrEmpty(item.BaseClassName)) typeDec.BaseTypes.Add(new CodeTypeReference(item.BaseClassName)); else if (!String.IsNullOrEmpty(BaseClassName)) typeDec.BaseTypes.Add(new CodeTypeReference(BaseClassName)); else typeDec.BaseTypes.Add(new CodeTypeReference(typeof(TemplateBase))); if (!String.IsNullOrEmpty(item.Name)) typeDec.LinePragma = new CodeLinePragma(item.Name, 1); // Render方法 CreateRenderMethod(item.Blocks, lineNumbers, typeDec); // 代码生成选项 CodeGeneratorOptions options = new CodeGeneratorOptions(); options.VerbatimOrder = true; options.BlankLinesBetweenMembers = false; options.BracingStyle = "C"; // 其它类成员代码块 Boolean firstMemberFound = false; foreach (Block block in item.Blocks) { firstMemberFound = GenerateMemberForBlock(block, typeDec, lineNumbers, provider, options, firstMemberFound); } // 模版变量 if (item.Vars != null && item.Vars.Count > 0) { // 构建静态构造函数,初始化静态属性Vars CreateCctorMethod(typeDec, item.Vars); //public Int32 VarName //{ // get { return (Int32)GetData("VarName"); } // set { Data["VarName"] = value; } //} foreach (String v in item.Vars.Keys) { TypeX vtype = TypeX.Create(item.Vars[v]); String codeName = vtype.FullName; StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.AppendFormat("public {0} {1}", codeName, v); sb.AppendLine("{"); sb.AppendFormat(" get {{ return GetData<{0}>(\"{1}\"); }}", codeName, v); sb.AppendLine(); sb.AppendFormat(" set {{ Data[\"{0}\"] = value; }}", v); sb.AppendLine(); sb.AppendLine("}"); CodeSnippetTypeMember member = new CodeSnippetTypeMember(sb.ToString()); typeDec.Members.Add(member); } } // 输出 using (StringWriter writer = new StringWriter()) { provider.GenerateCodeFromNamespace(codeNamespace, new IndentedTextWriter(writer), options); return writer.ToString(); } }
private TemplateItem ProcessDirective(Directive directive, TemplateItem item) { #region 包含include if (String.Equals(directive.Name, "include", StringComparison.OrdinalIgnoreCase)) { String name = directive.GetParameter("name"); // 可能采用了相对路径 if (!File.Exists(name)) name = Path.Combine(Path.GetDirectoryName(item.Name), name); String content = null; TemplateItem ti = FindTemplateItem(name); if (ti != null) { ti.Included = true; content = ti.Content; } else { // 尝试读取文件 if (File.Exists(name)) { ti = new TemplateItem(); ti.Name = name; ti.Content = File.ReadAllText(name); Templates.Add(ti); ti.Included = true; content = ti.Content; } } // 允许内容为空 //if (String.IsNullOrEmpty(content)) throw new TemplateException(directive.Block, String.Format("加载模版[{0}]失败!", name)); return ti; } #endregion if (String.Equals(directive.Name, "assembly", StringComparison.OrdinalIgnoreCase)) { String name = directive.GetParameter("name"); if (!AssemblyReferences.Contains(name)) AssemblyReferences.Add(name); } else if (String.Equals(directive.Name, "import", StringComparison.OrdinalIgnoreCase)) { item.Imports.Add(directive.GetParameter("namespace")); } else if (String.Equals(directive.Name, "template", StringComparison.OrdinalIgnoreCase)) { if (!item.Processed) { // 由模版指令指定类名 String name = directive.GetParameter("name"); if (!String.IsNullOrEmpty(name)) item.ClassName = name; //item.BaseClassName = directive.GetParameter("inherits"); if (directive.TryGetParameter("inherits", out name)) item.BaseClassName = name; item.Processed = true; } else throw new TemplateException(directive.Block, "多个模版指令!"); } else if (String.Equals(directive.Name, "var", StringComparison.OrdinalIgnoreCase)) { String name = directive.GetParameter("name"); String type = directive.GetParameter("type"); if (item.Vars.ContainsKey(name)) throw new TemplateException(directive.Block, "模版变量" + name + "已存在!"); Type ptype = TypeX.GetType(type, true); if (ptype == null) throw new TemplateException(directive.Block, "无法找到模版变量类型" + type + "!"); // 因为TypeX.GetType的强大,模版可能没有引用程序集和命名空间,甚至type位于未装载的程序集中它也会自动装载,所以这里需要加上 ImportType(item, ptype); item.Vars.Add(name, ptype); } return null; }
/// <summary>处理指令</summary> /// <param name="item"></param> /// <returns>返回指令集合</returns> private void ProcessDirectives(TemplateItem item) { // 使用包含堆栈处理包含,检测循环包含 Stack<String> includeStack = new Stack<String>(); includeStack.Push(item.Name); String[] directives = new String[] { "template", "assembly", "import", "include", "var" }; for (Int32 i = 0; i < item.Blocks.Count; i++) { Block block = item.Blocks[i]; if (block.Type != BlockType.Directive) continue; // 弹出当前块的模版名 while (includeStack.Count > 0 && !String.Equals(includeStack.Peek(), block.Name, StringComparison.OrdinalIgnoreCase)) { includeStack.Pop(); } Directive directive = TemplateParser.ParseDirectiveBlock(block); if (directive == null || Array.IndexOf(directives, directive.Name.ToLower()) < 0) throw new TemplateException(block, String.Format("无法识别的指令:{0}!", block.Text)); // 包含指令时,返回多个代码块 //List<Block> list = ProcessDirective(directive, item); TemplateItem ti = ProcessDirective(directive, item); if (ti == null) continue; //List<Block> list = TemplateParser.Parse(ti.Name, ti.Content); // 拆分成块 if (ti.Blocks == null || ti.Blocks.Count < 1) ti.Blocks = TemplateParser.Parse(ti.Name, ti.Content); if (ti.Blocks == null || ti.Blocks.Count < 1) continue; List<Block> list = ti.Blocks; String name = ti.Name; if (includeStack.Contains(name)) throw new TemplateException(block, String.Format("循环包含名为[{0}]的模版!", name)); includeStack.Push(name); // 包含模版并入当前模版 item.Blocks.InsertRange(i + 1, list); } }
/// <summary>处理指定模版</summary> /// <param name="item">模版项</param> private void Process(TemplateItem item) { // 拆分成块 item.Blocks = TemplateParser.Parse(item.Name, item.Content); // 处理指令 ProcessDirectives(item); //TemplateParser.StripExtraNewlines(item.Blocks); if (Imports != null) item.Imports.AddRange(Imports); //if (References != null) context.References.AddRange(References); // 生成代码 item.Source = ConstructGeneratorCode(item, Debug, NameSpace, Provider); }
/// <summary> /// 添加模版项,实际上是添加到Templates集合中。 /// 未指定模版名称时,使用模版的散列作为模版名称 /// </summary> /// <param name="name"></param> /// <param name="content"></param> public void AddTemplateItem(String name, String content) { if (String.IsNullOrEmpty(name) && String.IsNullOrEmpty(content)) throw new ArgumentNullException("content", "名称和模版内容不能同时为空!"); if (Status >= TemplateStatus.Process) throw new InvalidOperationException("模版已分析处理,不能再添加模版!"); // 未指定模版名称时,使用模版的散列作为模版名称 if (String.IsNullOrEmpty(name)) name = Hash(content); TemplateItem item = FindTemplateItem(name); if (item == null) { item = new TemplateItem(); Templates.Add(item); } item.Name = name; item.Content = content; // 设置类名 var cname = Path.GetFileNameWithoutExtension(name); // 如果无扩展的名称跟前面的名称不同,并且无扩展名称跟编码后的类名相同,则设置类型为无扩展名称 if (cname != name && cname == GetClassName(cname)) { // 如果没有别的模版项用这个类名,这里使用 if (!Templates.Any(t => t.ClassName == cname)) item.ClassName = cname; } // 设置程序集名,采用最后一级目录名 if (String.IsNullOrEmpty(AssemblyName)) { var dname = Path.GetDirectoryName(name); if (!String.IsNullOrEmpty(dname)) { dname = Path.GetFileName(dname); if (!String.IsNullOrEmpty(dname)) AssemblyName = dname; } } }
static String FindBlockCodeInItem(String name, Int32 lineNumber, TemplateItem item) { var nocmpName = String.IsNullOrEmpty(name); for (var i = 0; i < item.Blocks.Count; i++) { var line = item.Blocks[i].StartLine; if (line >= lineNumber && (nocmpName || item.Blocks[i].Name == name)) { // 错误所在段 var n = i; if (line > lineNumber) { n--; line = item.Blocks[n].StartLine; } var code = item.Blocks[n].Text; var codeLines = code.Split(new String[] { Environment.NewLine }, StringSplitOptions.None); var sb = new StringBuilder(); // 错误行在第一行,需要上一段的最后一行 if (n > 0 && line == lineNumber) { var code2 = item.Blocks[n - 1].Text; var codeLines2 = code2.Split(new String[] { Environment.NewLine }, StringSplitOptions.None); sb.AppendLine((lineNumber - 1) + ":" + codeLines2[codeLines2.Length - 1]); } // 错误行代码段 { // 错误行不在第一行,需要上一行 if (line < lineNumber) sb.AppendLine((lineNumber - 1) + ":" + codeLines[lineNumber - line - 1]); // 错误行 sb.AppendLine(lineNumber + ":" + codeLines[lineNumber - line]); // 错误行不在最后一行,需要下一行 if (line + codeLines.Length > lineNumber) sb.AppendLine((lineNumber + 1) + ":" + codeLines[lineNumber - line + 1]); } // 错误行在最后一行以后的,需要下一段的第一行 if (n < item.Blocks.Count - 1 && line + codeLines.Length <= lineNumber) { var code2 = item.Blocks[n + 1].Text; var codeLines2 = code2.Split(new String[] { Environment.NewLine }, StringSplitOptions.None); sb.AppendLine((lineNumber + 1) + ":" + codeLines2[0]); } return sb.ToString(); } } return null; }