在上一篇文章中介绍了dasBlog模板引擎的两个概念Theme和Macro。这一篇文章介绍dasBlog模板引擎的运行过程。
    先简单概括一下dasBlog模板引擎的原理。它提供了一个ShareBasePage的页基类,所有的页面类都从这个类派生,在这个类里,进行页面状态的传递和最终页面的生成。可以说,这是整个模板引擎的一个全局控制者。生成的方法是:在ShareBasePage里读取相应的template文件,顺序提取其中的HTML代码和宏进行相应的处理后添加到页面的PlaceHolder中。HTML代码是被添加到Literal后添加,取得宏表达式后,会转换成相应的Control后再添加到PlaceHolder。这就是页面生成的整个过程。

      我们先来看看SharedBasePage的构造函数。从里面两个函数名就可以看出,进行的是状态加载和页面初始化的任务。

        public SharedBasePage()
        
{
                          
            Init 
+= new EventHandler(this.SessionLoad);
            Init 
+= new EventHandler(this.SetupPage);
            
        }

      SharedBasePager中的Property添加了自定义的PersistentPageStateAttribute属性之后,每次页面加载完成,该属性都会被保存在Cookie中,第一次开始加载的时候又会被读出来。这个读取来的工作就在SessionLoad里完成了,而保存的工作就在SessionStore里完成了。
而SetupPage做的是些全球和参数处理方面的操作。在SetupPage中还完成了一个很重要的工作,加载了Macros类,这个类里包含了一些全局宏。所谓全局宏是指:网站名称,Trackback排行榜等在任何一个页面都会被显示的内容。相对的,有继承自Macros类的DayMacros(显示某日文章列表的页面的宏的集合),ItemMacros(显示单篇文章的页面宏的集合),EditMacros(文章编辑页面的宏的集合)。

     当SharedBasePage初始化完成之后,就开始正式进入页面生成步骤了。在SharedBasePage里ProcessTemplate方法是做这个工作的。以下是它的源码:

        public virtual void ProcessTemplate()
        
{
            TemplateProcessor templateProcessor 
= new TemplateProcessor();
            
string path = Request.PhysicalApplicationPath;
                 //取得当前使用Template对应的文件内容
            
string templateString = GetPageTemplate(path);
            
            Match match 
= findBodyTag.Match(templateString);
            
if ( match.Success )
            
{
                
// this section splits the template into a header, body and footer section
                
// (all above and including <body>, everything between <body></body> and all below and including </body>
                
//找到<body>的位置
                int indexBody = templateString.IndexOf("</body>");
                
if ( indexBody == -1 )
                
{
                    indexBody 
= templateString.IndexOf("</BODY>");
                }


                
//取得<head>部分
                
// the header template contains everything above and including the body tag
                string headerTemplate=templateString.Substring(0,match.Index+match.Length);
                
                
// insert necessary headtags and fix stylesheet relative links
                
// fix any relative css link tags
                
//转换地址
                headerTemplate = hrefRegEx.Replace(headerTemplate, String.Format("href=\"{0}themes", Utils.GetBaseUrl()));

                
string baseTag = String.Format("<base href=\"{0}\"></base>\r\n", Utils.GetBaseUrl());
                
string linkTag = String.Format("<link rel=\"alternate\" type=\"application/rss+xml\" title=\"{2}\" href=\"{0}\" />\r\n<link rel=\"alternate\" type=\"application/atom+xml\" title=\"{2}\" href=\"{1}\" />\r\n", Utils.GetRssUrl(),Utils.GetAtomUrl(),HttpUtility.HtmlEncode(siteConfig.Title));

                
int indexHead = headerTemplate.IndexOf("</head>");
                
if ( indexHead == -1 )
                
{
                    indexHead 
= headerTemplate.IndexOf("</HEAD>");
                }

                // 给<head>部分添加css等
                headerTemplate = headerTemplate.Insert(indexHead, baseTag.ToString() + linkTag.ToString());

                
// therefore it must close with a closing angle bracket, but it's better to check 
                if ( headerTemplate[headerTemplate.Length-1== '>' )
                
{
                    
// if that's so, we want to inject the reading order designator if we're right-to-left
                    
// or it's explicitly specified
                    string pageReadingDirection = coreStringTables.GetString("page_reading_direction");
                    
if ( pageReadingDirection != null && pageReadingDirection.Length > 0 )
                    
{
                        
if (pageReadingDirection == "RTL"this.readingDirection = TextDirection.RightToLeft; 
                        headerTemplate 
= headerTemplate.Substring(0, headerTemplate.Length-1+ " dir=\"" + pageReadingDirection + "\">";
                    }

                }


                
string bodyTemplate,footerTemplate;
                
if( indexBody != -1 )
                
{
                        //取得template中的主体部分
                    bodyTemplate
=templateString.Substring(match.Index+match.Length,indexBody-(match.Index+match.Length));
                        // 取得template中的footer部分
                    footerTemplate
=templateString.Substring(indexBody);
                }

                
else
                
{
                    bodyTemplate
=templateString.Substring(match.Index+match.Length);
                    footerTemplate
="";
                }

                        
                
// 对headerTemplate进行宏处理,添加到页面的PlaceHolder中
                templateProcessor.ProcessTemplate( this, headerTemplate, ContentPlaceHolder, macros );
                
                  // 初始化一个Form
                BaseHtmlForm mainForm = new BaseHtmlForm();
                mainForm.ID 
= "mainForm";
                  // 将mainForm添加到PlaceHolder中
                ContentPlaceHolder.Controls.Add(mainForm);
                
// 对bodyTemplate进行宏处理,添加到mainForm中
                templateProcessor.ProcessTemplate( this, bodyTemplate, mainForm, macros );
               // 对footerTemplate进行宏处理,添加到页面的PlaceHolder中

                if ( footerTemplate.Length > 0 )
                
{
                    templateProcessor.ProcessTemplate( 
this, footerTemplate, ContentPlaceHolder, macros );
                }

            }

            
else
            
{
                
// if the page is just an unrecognizable mess of tags, process in one shot.
                templateProcessor.ProcessTemplate( this, templateString, ContentPlaceHolder, macros );
            }

            
        }

在这个方法里,只是进行了整个生成流程的控制,第一步工作当然是取得模板文件了。

string templateString = GetPageTemplate(path);

模板文件的获取是在Theme类里面完成了的。Theme里的OpenTemplate()函数返回一个TextReader对象,在SharedBasePage里读成string,页面生成的过程就是对这个string进行操作了。而真正生成页面的工作是在TemplateProcessor这个类里完成的。而TemplateProcessor里起主要作用的其实也只是一个ProcessTemplate方法,其代码如下:

        public void ProcessTemplate(SharedBasePage page, Entry entry, string templateString, Control contentPlaceHolder, Macros macros)
        
{
            
int lastIndex = 0;

            MatchCollection matches 
= templateFinder.Matches(templateString);
            
foreach( Match match in matches )
            
{
                
//添加宏之外的其它字符串
                if ( match.Index > lastIndex )
                
{
                    contentPlaceHolder.Controls.Add(
new LiteralControl(templateString.Substring(lastIndex,match.Index-lastIndex)));
                }

                Group g 
= match.Groups["macro"];
                Capture c 
= g.Captures[0];
                
                Control ctrl 
= null;
                
object targetMacroObj = macros;
                
string captureValue = c.Value;
                
                
//Check for a string like: <%foo("bar", "bar")|assemblyConfigName%>
                int assemblyNameIndex = captureValue.IndexOf(")|");
                
//是否是自定义宏
                if (assemblyNameIndex != -1//use the default Macros
                {
                    
//The QN minus the )| 
                    
//取得自定义assemblyName
                    string macroAssemblyName = captureValue.Substring(assemblyNameIndex+2);
                    
//The method, including the )
                    
//取得宏名称
                    captureValue = captureValue.Substring(0,assemblyNameIndex+1);

                    
//生成自定义宏对象
                    try
                    
{
                        targetMacroObj 
= MacrosFactory.CreateCustomMacrosInstance(page, entry, macroAssemblyName);
                    }

                    
catch (Exception ex)
                    
{
                        page.LoggingService.AddEvent(
new EventDataItem(EventCodes.Error,String.Format("Error executing Macro: {0}",ex.ToString()),string.Empty));
                    }

                }


                
try
                
{
                    
//执行宏,返回一个对象
                    ctrl = InvokeMacro(targetMacroObj,captureValue) as Control;
                    
if (ctrl != null)
                    
{
                        contentPlaceHolder.Controls.Add(ctrl);
                    }

                    
else 
                    
{
                        page.LoggingService.AddEvent(
new EventDataItem(EventCodes.Error,String.Format("Error executing Macro: {0} returned null.",captureValue),string.Empty));
                    }

                }

                
catch (Exception ex)
                
{
                    
string error = String.Format("Error executing macro: {0}. Make sure it you're calling it in your BlogTemplate with paratheses like 'myMacro()'. Macros with parameter lists and overloads must be called in this way. Exception: {1}",c.Value, ex.ToString());
                    page.LoggingService.AddEvent(
new EventDataItem(EventCodes.Error,error,string.Empty));
                }

                lastIndex 
= match.Index+match.Length;
            }

            
if ( lastIndex < templateString.Length)
            
{
                
//将宏添加到PlacheHolder中
                contentPlaceHolder.Controls.Add(new LiteralControl(templateString.Substring(lastIndex,templateString.Length-lastIndex)));
            }

        }

这里的做的工作就是匹配所有类似<%SiteName%>这样的字符串,把它转化成相应的Control,我把它叫做“执行宏”,执行宏的工作在这里进行:

ctrl = InvokeMacro(targetMacroObj,captureValue) as Control;

以下是InvodeMacro代码:

        private object InvokeMacro( object obj, string expression )
        
{
                
//子字符串的开始位置
                int subexStartIndex = 0;
                
//子字符串的结束位置
                int subexEndIndex = 0;
                
object subexObject = obj;
                
            
try
            
{
                
do
                
{
                    subexEndIndex 
= expression.IndexOfAny(new char[]{'.','(','['}, subexStartIndex);
                    
                    
if ( subexEndIndex == -1 ) subexEndIndex=expression.Length;
                    
//取得当次循环处理的宏的字符串表达式
                    string subex = expression.Substring(subexStartIndex,subexEndIndex-subexStartIndex);
                    
//为下次循环做准备
                    subexStartIndex = subexEndIndex+1;
                    MemberInfo memberToInvoke;
                    
//搜索与子字符串匹配的对象或方法
                    MemberInfo[] members = subexObject.GetType().FindMembers(
                        MemberTypes.Field
|MemberTypes.Method|MemberTypes.Property,
                        BindingFlags.IgnoreCase
|BindingFlags.Instance|BindingFlags.Public,
                        
new MemberFilter(this.IsMemberEligibleForMacroCall), subex.Trim() );
                    
string[] arglist=null;

                    
if ( members.Length == 0 )
                    
{
                        
throw new MissingMemberException(subexObject.GetType().FullName,subex.Trim());
                    }


                    
//当当前宏带有参数时,取得当前宏的参数
                    if ( subexEndIndex<expression.Length && (expression[subexEndIndex] == '[' || expression[subexEndIndex] == '(') )
                    
{
                        arglist 
= SplitArgs(expression,subexEndIndex, ref subexStartIndex);
                    }


                    
//SDH: We REALLY need to refactor this whole Clemens thing - it's getting hairy.
                    memberToInvoke = null;
                    
if ( members.Length > 1 )
                    
{
                        
foreach(MemberInfo potentialMember in members)
                        
{
                            MethodInfo potentialMethod 
= potentialMember as MethodInfo;
                            
if(potentialMethod != null)
                            
{
                                ParameterInfo[] parameters 
= potentialMethod.GetParameters();
                                
if(parameters != null && parameters.Length > 0)
                                
{
                                    
//当参数数量相同时判定为要执行的宏
                                    if(parameters.Length == arglist.Length)
                                    
{
                                        memberToInvoke 
= potentialMember;
                                        
break;
                                    }

                                }

                            }

                        }

                    }


                    
//如果不存在该方法,则使用第一个
                    if(memberToInvoke == null)//Previous behavior, use the first one.
                    {
                        memberToInvoke 
= members[0];
                    }



                    
//当要执行的宏是一个属性,类似这样的东西Lupin.或者LUPIN[a,b]
                    if ( memberToInvoke.MemberType == MemberTypes.Property &&
                        (subexEndIndex
==expression.Length ||
                        expression[subexEndIndex] 
== '.' ||
                        expression[subexEndIndex] 
== '[' ))
                    
{
                        PropertyInfo propInfo 
= memberToInvoke as PropertyInfo;
                        
if ( subexEndIndex<expression.Length && expression[subexEndIndex] == '[' )
                        
{
                            System.Reflection.ParameterInfo[] paramInfo 
= propInfo.GetIndexParameters();
                            
if ( arglist.Length > paramInfo.Length )
                            
{
                                
throw new InvalidOperationException(String.Format("Parameter list length mismatch {0}",memberToInvoke.Name));
                            }

                            
object[] oarglist = new object[paramInfo.Length];
                            
forint n=0;n<arglist.Length;n++)
                            
{
                                oarglist[n] 
= Convert.ChangeType(arglist[n],paramInfo[n].ParameterType);
                            }

                            subexObject 
= propInfo.GetValue(subexObject,oarglist);
                        }

                        
else
                        
{
                            subexObject 
= propInfo.GetValue(subexObject,null);
                        }

                    }

                    
else if ( memberToInvoke.MemberType == MemberTypes.Field &&
                        (subexEndIndex
==expression.Length ||
                        expression[subexEndIndex] 
== '.'))
                    
{
                        FieldInfo fieldInfo 
= memberToInvoke as FieldInfo;
                        subexObject 
= fieldInfo.GetValue(subexObject);
                    }

                    
else if ( memberToInvoke.MemberType == MemberTypes.Method &&
                                subexEndIndex
<expression.Length && expression[subexEndIndex] == '(' )
                    
{
                        MethodInfo methInfo 
= memberToInvoke as MethodInfo;
                        System.Reflection.ParameterInfo[] paramInfo 
= methInfo.GetParameters();
                        
if ( arglist.Length > paramInfo.Length && 
                            
!(paramInfo.Length>0 && paramInfo[paramInfo.Length-1].ParameterType == typeof(string[])))
                        
{
                            
throw new InvalidOperationException(String.Format("Parameter list length mismatch {0}",memberToInvoke.Name));
                        }

                        
object[] oarglist = new object[paramInfo.Length];
                        
forint n=0;n<arglist.Length;n++)
                        
{
                            
if ( n == paramInfo.Length-1 && 
                                arglist.Length
>paramInfo.Length)
                            
{
                                
string[] paramsArg = new string[arglist.Length-paramInfo.Length+1];
                                
forint m=n;m<arglist.Length;m++)
                                
{
                                    paramsArg[m
-n] = Convert.ChangeType(arglist[n],typeof(string)) as string;
                                }

                                oarglist[n] 
= paramsArg;
                                
break;
                            }

                            
else
                            
{
                                oarglist[n] 
= Convert.ChangeType(arglist[n],paramInfo[n].ParameterType);
                            }

                        }

                        subexObject 
= methInfo.Invoke(subexObject,oarglist);
                    }

                    
                }

                
while(subexEndIndex<expression.Length && subexStartIndex < expression.Length);

                
return subexObject==null?new LiteralControl(""):subexObject;
            }

            
catch( Exception exc )
            
{
                ErrorTrace.Trace(System.Diagnostics.TraceLevel.Error,exc);
                
throw;
            }

            
//return new LiteralControl("");
        }

这个函数相当于是个超简单的脚本解释引擎,具体的工作流程已经超出来本篇的讨论内容,而且再说下去这篇文章就太长了。。所以,就此打住。dasBlog的模板引擎的实现也解释得差不多了。

posted on 2006-03-30 21:44  Lupin  阅读(649)  评论(0编辑  收藏  举报