2 // System.Web.Compilation.TemplateControlCompiler
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2003 Ximian, Inc (http://www.ximian.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.ComponentModel;
35 using System.Globalization;
36 using System.Reflection;
40 using System.Web.Util;
41 using System.ComponentModel.Design.Serialization;
43 using System.Collections.Specialized;
44 using System.Text.RegularExpressions;
47 namespace System.Web.Compilation
49 class TemplateControlCompiler : BaseCompiler
51 static BindingFlags noCaseFlags = BindingFlags.Public | BindingFlags.NonPublic |
52 BindingFlags.Instance | BindingFlags.IgnoreCase;
54 TemplateControlParser parser;
56 ILocation currentLocation;
58 static TypeConverter colorConverter;
60 static CodeVariableReferenceExpression ctrlVar = new CodeVariableReferenceExpression ("__ctrl");
63 static Regex bindRegex = new Regex (@"Bind\s*\(""(.*?)""\)\s*%>", RegexOptions.Compiled);
66 public TemplateControlCompiler (TemplateControlParser parser)
72 void EnsureID (ControlBuilder builder)
74 if (builder.ID == null || builder.ID.Trim () == "")
75 builder.ID = builder.GetNextID (null);
78 void CreateField (ControlBuilder builder, bool check)
81 if (partialNameOverride [builder.ID] != null)
84 currentLocation = builder.location;
85 if (check && CheckBaseFieldOrProperty (builder.ID, builder.ControlType))
86 return; // The field or property already exists in a base class and is accesible.
88 CodeMemberField field;
89 field = new CodeMemberField (builder.ControlType.FullName, builder.ID);
90 field.Attributes = MemberAttributes.Family;
91 mainClass.Members.Add (field);
94 bool CheckBaseFieldOrProperty (string id, Type type)
96 FieldInfo fld = parser.BaseType.GetField (id, noCaseFlags);
99 if (fld == null || fld.IsPrivate) {
100 PropertyInfo prop = parser.BaseType.GetProperty (id, noCaseFlags);
102 MethodInfo setm = prop.GetSetMethod (true);
104 other = prop.PropertyType;
107 other = fld.FieldType;
113 if (!other.IsAssignableFrom (type)) {
114 string msg = String.Format ("The base class includes the field '{0}', but its " +
115 "type '{1}' is not compatible with {2}",
117 throw new ParseException (currentLocation, msg);
123 void AddParsedSubObjectStmt (ControlBuilder builder, CodeExpression expr)
125 if (!builder.haveParserVariable) {
126 CodeVariableDeclarationStatement p = new CodeVariableDeclarationStatement();
128 p.Type = new CodeTypeReference (typeof (IParserAccessor));
129 p.InitExpression = new CodeCastExpression (typeof (IParserAccessor), ctrlVar);
130 builder.method.Statements.Add (p);
131 builder.haveParserVariable = true;
134 CodeVariableReferenceExpression var = new CodeVariableReferenceExpression ("__parser");
135 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (var, "AddParsedSubObject");
136 invoke.Parameters.Add (expr);
137 builder.method.Statements.Add (invoke);
140 void InitMethod (ControlBuilder builder, bool isTemplate, bool childrenAsProperties)
142 string tailname = ((builder is RootBuilder) ? "Tree" : ("_" + builder.ID));
143 CodeMemberMethod method = new CodeMemberMethod ();
144 builder.method = method;
145 method.Name = "__BuildControl" + tailname;
146 method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
147 Type type = builder.ControlType;
149 if (builder.HasAspCode) {
150 CodeMemberMethod renderMethod = new CodeMemberMethod ();
151 builder.renderMethod = renderMethod;
152 renderMethod.Name = "__Render" + tailname;
153 renderMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
154 CodeParameterDeclarationExpression arg1 = new CodeParameterDeclarationExpression ();
155 arg1.Type = new CodeTypeReference (typeof (HtmlTextWriter));
156 arg1.Name = "__output";
157 CodeParameterDeclarationExpression arg2 = new CodeParameterDeclarationExpression ();
158 arg2.Type = new CodeTypeReference (typeof (Control));
159 arg2.Name = "parameterContainer";
160 renderMethod.Parameters.Add (arg1);
161 renderMethod.Parameters.Add (arg2);
162 mainClass.Members.Add (renderMethod);
165 if (childrenAsProperties || builder.ControlType == null) {
167 if (builder.ControlType != null && builder.isProperty &&
168 !typeof (ITemplate).IsAssignableFrom (builder.ControlType))
169 typeString = builder.ControlType.FullName;
171 typeString = "System.Web.UI.Control";
173 method.Parameters.Add (new CodeParameterDeclarationExpression (typeString, "__ctrl"));
176 if (typeof (Control).IsAssignableFrom (type))
177 method.ReturnType = new CodeTypeReference (typeof (Control));
179 CodeObjectCreateExpression newExpr = new CodeObjectCreateExpression (type);
181 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
182 if (atts != null && atts.Length > 0) {
183 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
185 newExpr.Parameters.Add (new CodePrimitiveExpression (builder.TagName));
186 } else if (builder is DataBindingBuilder) {
187 newExpr.Parameters.Add (new CodePrimitiveExpression (0));
188 newExpr.Parameters.Add (new CodePrimitiveExpression (1));
191 method.Statements.Add (new CodeVariableDeclarationStatement (builder.ControlType, "__ctrl"));
192 CodeAssignStatement assign = new CodeAssignStatement ();
193 assign.Left = ctrlVar;
194 assign.Right = newExpr;
195 method.Statements.Add (assign);
197 CodeFieldReferenceExpression builderID = new CodeFieldReferenceExpression ();
198 builderID.TargetObject = thisRef;
199 builderID.FieldName = builder.ID;
200 assign = new CodeAssignStatement ();
201 assign.Left = builderID;
202 assign.Right = ctrlVar;
203 method.Statements.Add (assign);
204 if (typeof (UserControl).IsAssignableFrom (type)) {
205 CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression ();
206 mref.TargetObject = builderID;
207 mref.MethodName = "InitializeAsUserControl";
208 CodeMethodInvokeExpression initAsControl = new CodeMethodInvokeExpression (mref);
209 initAsControl.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page"));
210 method.Statements.Add (initAsControl);
213 if (typeof (System.Web.UI.WebControls.ContentPlaceHolder).IsAssignableFrom (type)) {
214 CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders");
215 CodeMethodInvokeExpression addPlaceholder = new CodeMethodInvokeExpression (prop, "Add");
216 addPlaceholder.Parameters.Add (ctrlVar);
217 method.Statements.Add (addPlaceholder);
222 mainClass.Members.Add (method);
225 void AddLiteralSubObject (ControlBuilder builder, string str)
227 if (!builder.HasAspCode) {
228 CodeObjectCreateExpression expr;
229 expr = new CodeObjectCreateExpression (typeof (LiteralControl), new CodePrimitiveExpression (str));
230 AddParsedSubObjectStmt (builder, expr);
232 CodeMethodReferenceExpression methodRef = new CodeMethodReferenceExpression ();
233 methodRef.TargetObject = new CodeArgumentReferenceExpression ("__output");
234 methodRef.MethodName = "Write";
236 CodeMethodInvokeExpression expr;
237 expr = new CodeMethodInvokeExpression (methodRef, new CodePrimitiveExpression (str));
238 builder.renderMethod.Statements.Add (expr);
242 string TrimDB (string value)
244 string str = value.Trim ();
245 str = str.Substring (3);
246 return str.Substring (0, str.Length - 2);
249 string DataBoundProperty (ControlBuilder builder, Type type, string varName, string value)
251 value = TrimDB (value);
252 CodeMemberMethod method;
253 string dbMethodName = builder.method.Name + "_DB_" + dataBoundAtts++;
255 method = CreateDBMethod (dbMethodName, GetContainerType (builder), builder.ControlType);
257 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
259 // This should be a CodePropertyReferenceExpression for properties... but it works anyway
260 CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (targetExpr, varName);
263 if (type == typeof (string)) {
264 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
265 CodeTypeReferenceExpression conv = new CodeTypeReferenceExpression (typeof (Convert));
266 tostring.Method = new CodeMethodReferenceExpression (conv, "ToString");
267 tostring.Parameters.Add (new CodeSnippetExpression (value));
270 CodeSnippetExpression snippet = new CodeSnippetExpression (value);
271 expr = new CodeCastExpression (type, snippet);
274 method.Statements.Add (new CodeAssignStatement (field, expr));
275 mainClass.Members.Add (method);
279 void AddCodeForPropertyOrField (ControlBuilder builder, Type type, string var_name, string att, MemberInfo member, bool isDataBound)
281 CodeMemberMethod method = builder.method;
283 string dbMethodName = DataBoundProperty (builder, type, var_name, att);
284 AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName);
288 CodeAssignStatement assign = new CodeAssignStatement ();
289 assign.Left = new CodePropertyReferenceExpression (ctrlVar, var_name);
290 currentLocation = builder.location;
291 assign.Right = GetExpressionFromString (type, att, member);
293 method.Statements.Add (assign);
296 bool IsDataBound (string value)
298 if (value == null || value == "")
301 string str = value.Trim ();
302 return (StrUtils.StartsWith (str, "<%#") && StrUtils.EndsWith (str, "%>"));
306 void RegisterBindingInfo (ControlBuilder builder, string propName, ref string value)
308 string str = value.Trim ();
309 str = str.Substring (3).Trim (); // eats "<%#"
310 if (StrUtils.StartsWith (str, "Bind")) {
311 Match match = bindRegex.Match (str);
313 string bindingName = match.Groups [1].Value;
315 TemplateBuilder templateBuilder = builder.ParentTemplateBuilder;
316 if (templateBuilder == null || templateBuilder.BindingDirection == BindingDirection.OneWay)
317 throw new HttpException ("Bind expression not allowed in this context.");
319 string id = builder.attribs ["ID"] as string;
321 throw new HttpException ("Control of type '" + builder.ControlType + "' using two-way binding on property '" + propName + "' must have an ID.");
323 templateBuilder.RegisterBoundProperty (builder.ControlType, propName, id, bindingName);
324 value = "<%# Eval" + str.Substring (4);
331 static bool InvariantCompare (string a, string b)
333 return (0 == String.Compare (a, b, false, CultureInfo.InvariantCulture));
337 static bool InvariantCompareNoCase (string a, string b)
339 return (0 == String.Compare (a, b, true, CultureInfo.InvariantCulture));
342 static MemberInfo GetFieldOrProperty (Type type, string name)
344 MemberInfo member = null;
346 member = type.GetProperty (name, noCaseFlags & ~BindingFlags.NonPublic);
353 member = type.GetField (name, noCaseFlags & ~BindingFlags.NonPublic);
359 bool ProcessPropertiesAndFields (ControlBuilder builder, MemberInfo member, string id,
360 string attValue, string prefix)
362 int hyphen = id.IndexOf ('-');
363 bool isPropertyInfo = (member is PropertyInfo);
364 bool isDataBound = IsDataBound (attValue);
367 if (isPropertyInfo) {
368 type = ((PropertyInfo) member).PropertyType;
369 if (hyphen == -1 && ((PropertyInfo) member).CanWrite == false)
372 type = ((FieldInfo) member).FieldType;
375 if (InvariantCompareNoCase (member.Name, id)) {
377 if (isDataBound) RegisterBindingInfo (builder, member.Name, ref attValue);
379 AddCodeForPropertyOrField (builder, type, member.Name, attValue, member, isDataBound);
386 string prop_field = id.Replace ("-", ".");
387 string [] parts = prop_field.Split (new char [] {'.'});
388 int length = parts.Length;
389 if (length < 2 || !InvariantCompareNoCase (member.Name, parts [0]))
393 MemberInfo sub_member = GetFieldOrProperty (type, parts [1]);
394 if (sub_member == null)
397 string new_prefix = prefix + parts [0] + ".";
398 string new_id = id.Substring (hyphen + 1);
399 return ProcessPropertiesAndFields (builder, sub_member, new_id, attValue, new_prefix);
402 MemberInfo subpf = GetFieldOrProperty (type, parts [1]);
403 if (!(subpf is PropertyInfo))
406 PropertyInfo subprop = (PropertyInfo) subpf;
407 if (subprop.CanWrite == false)
410 bool is_bool = (subprop.PropertyType == typeof (bool));
411 if (!is_bool && attValue == null)
412 return false; // Font-Size -> Font-Size="" as html
414 string val = attValue;
415 if (attValue == null && is_bool)
416 val = "true"; // Font-Bold <=> Font-Bold="true"
418 if (isDataBound) RegisterBindingInfo (builder, prefix + member.Name + "." + subprop.Name, ref attValue);
420 AddCodeForPropertyOrField (builder, subprop.PropertyType,
421 prefix + member.Name + "." + subprop.Name,
422 val, subprop, isDataBound);
427 void AddEventAssign (CodeMemberMethod method, string name, Type type, string value)
429 //"__ctrl.{0} += new {1} (this.{2});"
430 CodeEventReferenceExpression evtID = new CodeEventReferenceExpression (ctrlVar, name);
432 CodeDelegateCreateExpression create;
433 create = new CodeDelegateCreateExpression (new CodeTypeReference (type), thisRef, value);
435 CodeAttachEventStatement attach = new CodeAttachEventStatement (evtID, create);
436 method.Statements.Add (attach);
439 void CreateAssignStatementsFromAttributes (ControlBuilder builder)
441 this.dataBoundAtts = 0;
442 IDictionary atts = builder.attribs;
443 if (atts == null || atts.Count == 0)
446 EventInfo [] ev_info = null;
447 bool is_processed = false;
448 Type type = builder.ControlType;
450 foreach (string id in atts.Keys) {
451 if (InvariantCompareNoCase (id, "runat"))
454 is_processed = false;
455 string attvalue = atts [id] as string;
456 if (id.Length > 2 && id.Substring (0, 2).ToUpper () == "ON"){
458 ev_info = type.GetEvents ();
460 string id_as_event = id.Substring (2);
461 foreach (EventInfo ev in ev_info){
462 if (InvariantCompareNoCase (ev.Name, id_as_event)){
463 AddEventAssign (builder.method,
477 int hyphen = id.IndexOf ('-');
480 alt_id = id.Substring (0, hyphen);
482 MemberInfo fop = GetFieldOrProperty (type, alt_id);
484 is_processed = ProcessPropertiesAndFields (builder, fop, id, attvalue, null);
492 if (!typeof (IAttributeAccessor).IsAssignableFrom (type))
493 throw new ParseException (builder.location, "Unrecognized attribute: " + id);
496 CodeMemberMethod method = builder.method;
497 string val = (string) atts [id];
498 bool databound = IsDataBound (val);
500 val = val.Substring (3);
501 val = val.Substring (0, val.Length - 2);
502 CreateDBAttributeMethod (builder, id, val);
504 CodeCastExpression cast;
505 CodeMethodReferenceExpression methodExpr;
506 CodeMethodInvokeExpression expr;
508 cast = new CodeCastExpression (typeof (IAttributeAccessor), ctrlVar);
509 methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
510 expr = new CodeMethodInvokeExpression (methodExpr);
511 expr.Parameters.Add (new CodePrimitiveExpression (id));
512 expr.Parameters.Add (new CodePrimitiveExpression ((string) atts [id]));
513 method.Statements.Add (expr);
518 void CreateDBAttributeMethod (ControlBuilder builder, string attr, string code)
520 if (code == null || code.Trim () == "")
523 string id = builder.GetNextID (null);
524 string dbMethodName = "__DataBind_" + id;
525 CodeMemberMethod method = builder.method;
526 AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName);
528 method = CreateDBMethod (dbMethodName, GetContainerType (builder), builder.ControlType);
529 CodeCastExpression cast;
530 CodeMethodReferenceExpression methodExpr;
531 CodeMethodInvokeExpression expr;
533 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
534 cast = new CodeCastExpression (typeof (IAttributeAccessor), targetExpr);
535 methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
536 expr = new CodeMethodInvokeExpression (methodExpr);
537 expr.Parameters.Add (new CodePrimitiveExpression (attr));
538 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
539 tostring.Method = new CodeMethodReferenceExpression (
540 new CodeTypeReferenceExpression (typeof (Convert)),
542 tostring.Parameters.Add (new CodeSnippetExpression (code));
543 expr.Parameters.Add (tostring);
544 method.Statements.Add (expr);
545 mainClass.Members.Add (method);
548 void AddRenderControl (ControlBuilder builder)
550 CodeIndexerExpression indexer = new CodeIndexerExpression ();
551 indexer.TargetObject = new CodePropertyReferenceExpression (
552 new CodeArgumentReferenceExpression ("parameterContainer"),
555 indexer.Indices.Add (new CodePrimitiveExpression (builder.renderIndex));
557 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (indexer, "RenderControl");
558 invoke.Parameters.Add (new CodeArgumentReferenceExpression ("__output"));
559 builder.renderMethod.Statements.Add (invoke);
560 builder.renderIndex++;
563 void AddChildCall (ControlBuilder parent, ControlBuilder child)
565 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.method.Name);
566 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (m);
568 object [] atts = child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true);
569 if (atts != null && atts.Length > 0) {
570 PartialCachingAttribute pca = (PartialCachingAttribute) atts [0];
571 CodeTypeReferenceExpression cc = new CodeTypeReferenceExpression("System.Web.UI.StaticPartialCachingControl");
572 CodeMethodInvokeExpression build = new CodeMethodInvokeExpression (cc, "BuildCachedControl");
573 build.Parameters.Add (new CodeArgumentReferenceExpression("__ctrl"));
574 build.Parameters.Add (new CodePrimitiveExpression (child.ID));
577 build.Parameters.Add (new CodePrimitiveExpression (child.ControlType.GetHashCode ().ToString ()));
580 build.Parameters.Add (new CodePrimitiveExpression (Guid.NewGuid ().ToString ()));
582 build.Parameters.Add (new CodePrimitiveExpression (pca.Duration));
583 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByParams));
584 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByControls));
585 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByCustom));
586 build.Parameters.Add (new CodeDelegateCreateExpression (
587 new CodeTypeReference (typeof (System.Web.UI.BuildMethod)),
588 thisRef, child.method.Name));
590 parent.method.Statements.Add (build);
591 if (parent.HasAspCode)
592 AddRenderControl (parent);
596 if (child.isProperty || parent.ChildrenAsProperties) {
597 expr.Parameters.Add (new CodeFieldReferenceExpression (ctrlVar, child.TagName));
598 parent.method.Statements.Add (expr);
602 parent.method.Statements.Add (expr);
603 CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, child.ID);
604 if (parent.ControlType == null || typeof (IParserAccessor).IsAssignableFrom (parent.ControlType)) {
605 AddParsedSubObjectStmt (parent, field);
607 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (ctrlVar, "Add");
608 invoke.Parameters.Add (field);
609 parent.method.Statements.Add (invoke);
612 if (parent.HasAspCode)
613 AddRenderControl (parent);
616 void AddTemplateInvocation (CodeMemberMethod method, string name, string methodName)
618 CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
620 CodeObjectCreateExpression newBuild = new CodeObjectCreateExpression (typeof (BuildTemplateMethod));
621 newBuild.Parameters.Add (new CodeMethodReferenceExpression (thisRef, methodName));
623 CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
624 newCompiled.Parameters.Add (newBuild);
626 CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
627 method.Statements.Add (assign);
631 void AddBindableTemplateInvocation (CodeMemberMethod method, string name, string methodName, string extractMethodName)
633 CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
635 CodeObjectCreateExpression newBuild = new CodeObjectCreateExpression (typeof (BuildTemplateMethod));
636 newBuild.Parameters.Add (new CodeMethodReferenceExpression (thisRef, methodName));
638 CodeObjectCreateExpression newExtract = new CodeObjectCreateExpression (typeof (ExtractTemplateValuesMethod));
639 newExtract.Parameters.Add (new CodeMethodReferenceExpression (thisRef, extractMethodName));
641 CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledBindableTemplateBuilder));
642 newCompiled.Parameters.Add (newBuild);
643 newCompiled.Parameters.Add (newExtract);
645 CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
646 method.Statements.Add (assign);
649 string CreateExtractValuesMethod (TemplateBuilder builder)
651 CodeMemberMethod method = new CodeMemberMethod ();
652 method.Name = "__ExtractValues_" + builder.ID;
653 method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
654 method.ReturnType = new CodeTypeReference (typeof(IOrderedDictionary));
656 CodeParameterDeclarationExpression arg = new CodeParameterDeclarationExpression ();
657 arg.Type = new CodeTypeReference (typeof (Control));
658 arg.Name = "__container";
659 method.Parameters.Add (arg);
660 mainClass.Members.Add (method);
662 CodeObjectCreateExpression newTable = new CodeObjectCreateExpression ();
663 newTable.CreateType = new CodeTypeReference (typeof(OrderedDictionary));
664 method.Statements.Add (new CodeVariableDeclarationStatement (typeof(OrderedDictionary), "__table", newTable));
665 CodeVariableReferenceExpression tableExp = new CodeVariableReferenceExpression ("__table");
667 if (builder.Bindings != null) {
668 foreach (TemplateBinding binding in builder.Bindings) {
669 CodeVariableDeclarationStatement dec = new CodeVariableDeclarationStatement (binding.ControlType, binding.ControlId);
670 method.Statements.Add (dec);
671 CodeVariableReferenceExpression cter = new CodeVariableReferenceExpression ("__container");
672 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (cter, "FindControl");
673 invoke.Parameters.Add (new CodePrimitiveExpression (binding.ControlId));
675 CodeAssignStatement assign = new CodeAssignStatement ();
676 CodeVariableReferenceExpression control = new CodeVariableReferenceExpression (binding.ControlId);
677 assign.Left = control;
678 assign.Right = new CodeCastExpression (binding.ControlType, invoke);
679 method.Statements.Add (assign);
681 CodeConditionStatement sif = new CodeConditionStatement ();
682 sif.Condition = new CodeBinaryOperatorExpression (control, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
684 assign = new CodeAssignStatement ();
685 assign.Left = new CodeIndexerExpression (tableExp, new CodePrimitiveExpression (binding.FieldName));
686 assign.Right = new CodePropertyReferenceExpression (control, binding.ControlProperty);
687 sif.TrueStatements.Add (assign);
688 method.Statements.Add (sif);
692 method.Statements.Add (new CodeMethodReturnStatement (tableExp));
696 void AddContentTemplateInvocation (ContentControlBuilder cbuilder, CodeMemberMethod method, string methodName)
698 CodeObjectCreateExpression newBuild = new CodeObjectCreateExpression (typeof (BuildTemplateMethod));
699 newBuild.Parameters.Add (new CodeMethodReferenceExpression (thisRef, methodName));
701 CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
702 newCompiled.Parameters.Add (newBuild);
704 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (thisRef, "AddContentTemplate");
705 invoke.Parameters.Add (new CodePrimitiveExpression (cbuilder.ContentPlaceHolderID));
706 invoke.Parameters.Add (newCompiled);
708 method.Statements.Add (invoke);
712 void AddCodeRender (ControlBuilder parent, CodeRenderBuilder cr)
714 if (cr.Code == null || cr.Code.Trim () == "")
718 CodeSnippetStatement code = new CodeSnippetStatement (cr.Code);
719 parent.renderMethod.Statements.Add (code);
723 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression ();
724 expr.Method = new CodeMethodReferenceExpression (
725 new CodeArgumentReferenceExpression ("__output"),
728 expr.Parameters.Add (new CodeSnippetExpression (cr.Code));
729 parent.renderMethod.Statements.Add (expr);
732 static Type GetContainerType (ControlBuilder builder)
734 TemplateBuilder tb = builder as TemplateBuilder;
735 if (tb != null && tb.ContainerType != null)
736 return tb.ContainerType;
738 Type type = builder.BindingContainerType;
740 PropertyInfo prop = type.GetProperty ("Items", noCaseFlags & ~BindingFlags.NonPublic);
744 Type ptype = prop.PropertyType;
745 if (!typeof (ICollection).IsAssignableFrom (ptype))
748 prop = ptype.GetProperty ("Item", noCaseFlags & ~BindingFlags.NonPublic);
752 return prop.PropertyType;
755 CodeMemberMethod CreateDBMethod (string name, Type container, Type target)
757 CodeMemberMethod method = new CodeMemberMethod ();
758 method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
760 method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "sender"));
761 method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (EventArgs), "e"));
763 CodeTypeReference containerRef = new CodeTypeReference (container);
764 CodeTypeReference targetRef = new CodeTypeReference (target);
766 CodeVariableDeclarationStatement decl = new CodeVariableDeclarationStatement();
767 decl.Name = "Container";
768 decl.Type = containerRef;
769 method.Statements.Add (decl);
771 decl = new CodeVariableDeclarationStatement();
772 decl.Name = "target";
773 decl.Type = targetRef;
774 method.Statements.Add (decl);
776 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
777 CodeAssignStatement assign = new CodeAssignStatement ();
778 assign.Left = targetExpr;
779 assign.Right = new CodeCastExpression (targetRef, new CodeArgumentReferenceExpression ("sender"));
780 method.Statements.Add (assign);
782 assign = new CodeAssignStatement ();
783 assign.Left = new CodeVariableReferenceExpression ("Container");
784 assign.Right = new CodeCastExpression (containerRef,
785 new CodePropertyReferenceExpression (targetExpr, "BindingContainer"));
786 method.Statements.Add (assign);
791 void AddDataBindingLiteral (ControlBuilder builder, DataBindingBuilder db)
793 if (db.Code == null || db.Code.Trim () == "")
797 CreateField (db, false);
799 string dbMethodName = "__DataBind_" + db.ID;
800 // Add the method that builds the DataBoundLiteralControl
801 InitMethod (db, false, false);
802 CodeMemberMethod method = db.method;
803 AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName);
804 method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
806 // Add the DataBind handler
807 method = CreateDBMethod (dbMethodName, GetContainerType (builder), typeof (DataBoundLiteralControl));
809 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
810 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
811 invoke.Method = new CodeMethodReferenceExpression (targetExpr, "SetDataBoundString");
812 invoke.Parameters.Add (new CodePrimitiveExpression (0));
814 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
815 tostring.Method = new CodeMethodReferenceExpression (
816 new CodeTypeReferenceExpression (typeof (Convert)),
818 tostring.Parameters.Add (new CodeSnippetExpression (db.Code));
819 invoke.Parameters.Add (tostring);
820 method.Statements.Add (invoke);
822 mainClass.Members.Add (method);
824 AddChildCall (builder, db);
827 void FlushText (ControlBuilder builder, StringBuilder sb)
830 AddLiteralSubObject (builder, sb.ToString ());
835 void CreateControlTree (ControlBuilder builder, bool inTemplate, bool childrenAsProperties)
838 bool isTemplate = (typeof (TemplateBuilder).IsAssignableFrom (builder.GetType ()));
839 if (!isTemplate && !inTemplate) {
840 CreateField (builder, true);
841 } else if (!isTemplate) {
842 builder.ID = builder.GetNextID (null);
843 CreateField (builder, false);
846 InitMethod (builder, isTemplate, childrenAsProperties);
847 if (!isTemplate || builder.GetType () == typeof (RootBuilder))
848 CreateAssignStatementsFromAttributes (builder);
850 if (builder.Children != null && builder.Children.Count > 0) {
851 ArrayList templates = null;
853 StringBuilder sb = new StringBuilder ();
854 foreach (object b in builder.Children) {
857 sb.Append ((string) b);
861 FlushText (builder, sb);
862 if (b is ObjectTagBuilder) {
863 ProcessObjectTag ((ObjectTagBuilder) b);
868 if (b is ContentControlBuilder) {
869 ContentControlBuilder cb = (ContentControlBuilder) b;
870 CreateControlTree (cb, false, true);
871 AddContentTemplateInvocation (cb, builder.method, cb.method.Name);
876 if (b is TemplateBuilder) {
877 if (templates == null)
878 templates = new ArrayList ();
884 if (b is CodeRenderBuilder) {
885 AddCodeRender (builder, (CodeRenderBuilder) b);
889 if (b is DataBindingBuilder) {
890 AddDataBindingLiteral (builder, (DataBindingBuilder) b);
894 if (b is ControlBuilder) {
895 ControlBuilder child = (ControlBuilder) b;
896 CreateControlTree (child, inTemplate, builder.ChildrenAsProperties);
897 AddChildCall (builder, child);
901 throw new Exception ("???");
904 FlushText (builder, sb);
906 if (templates != null) {
907 foreach (TemplateBuilder b in templates) {
908 CreateControlTree (b, true, false);
910 if (b.BindingDirection == BindingDirection.TwoWay) {
911 string extractMethod = CreateExtractValuesMethod (b);
912 AddBindableTemplateInvocation (builder.method, b.TagName, b.method.Name, extractMethod);
916 AddTemplateInvocation (builder.method, b.TagName, b.method.Name);
922 if (builder.defaultPropertyBuilder != null) {
923 ControlBuilder b = builder.defaultPropertyBuilder;
924 CreateControlTree (b, false, true);
925 AddChildCall (builder, b);
928 if (builder.HasAspCode) {
929 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
930 m.TargetObject = thisRef;
931 m.MethodName = builder.renderMethod.Name;
933 CodeDelegateCreateExpression create = new CodeDelegateCreateExpression ();
934 create.DelegateType = new CodeTypeReference (typeof (RenderMethod));
935 create.TargetObject = thisRef;
936 create.MethodName = builder.renderMethod.Name;
938 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
939 invoke.Method = new CodeMethodReferenceExpression (ctrlVar, "SetRenderMethodDelegate");
940 invoke.Parameters.Add (create);
942 builder.method.Statements.Add (invoke);
945 if (!childrenAsProperties && typeof (Control).IsAssignableFrom (builder.ControlType))
946 builder.method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
949 protected override void CreateMethods ()
951 base.CreateMethods ();
954 CreateControlTree (parser.RootBuilder, false, false);
955 CreateFrameworkInitializeMethod ();
958 void CreateFrameworkInitializeMethod ()
960 CodeMemberMethod method = new CodeMemberMethod ();
961 method.Name = "FrameworkInitialize";
962 method.Attributes = MemberAttributes.Family | MemberAttributes.Override;
963 AddStatementsToFrameworkInitialize (method);
964 mainClass.Members.Add (method);
967 protected virtual void AddStatementsToFrameworkInitialize (CodeMemberMethod method)
969 if (!parser.EnableViewState) {
970 CodeAssignStatement stmt = new CodeAssignStatement ();
971 stmt.Left = new CodePropertyReferenceExpression (thisRef, "EnableViewState");
972 stmt.Right = new CodePrimitiveExpression (false);
973 method.Statements.Add (stmt);
976 CodeMethodReferenceExpression methodExpr;
977 methodExpr = new CodeMethodReferenceExpression (thisRef, "__BuildControlTree");
978 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (methodExpr, thisRef);
979 method.Statements.Add (new CodeExpressionStatement (expr));
982 protected override void AddApplicationAndSessionObjects ()
984 foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.ApplicationObjects) {
985 CreateFieldForObject (tag.Type, tag.ObjectID);
986 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, true, false);
989 foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.SessionObjects) {
990 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, false, false);
994 protected void ProcessObjectTag (ObjectTagBuilder tag)
996 string fieldName = CreateFieldForObject (tag.Type, tag.ObjectID);
997 CreatePropertyForObject (tag.Type, tag.ObjectID, fieldName, false);
1000 void CreateProperties ()
1002 if (!parser.AutoEventWireup) {
1003 CreateAutoEventWireup ();
1005 CreateAutoHandlers ();
1008 CreateApplicationInstance ();
1009 CreateTemplateSourceDirectory ();
1012 void CreateTemplateSourceDirectory ()
1014 CodeMemberProperty prop = new CodeMemberProperty ();
1015 prop.Type = new CodeTypeReference (typeof (string));
1016 prop.Name = "TemplateSourceDirectory";
1017 prop.Attributes = MemberAttributes.Public | MemberAttributes.Override;
1019 CodePrimitiveExpression expr = new CodePrimitiveExpression (parser.BaseVirtualDir);
1020 prop.GetStatements.Add (new CodeMethodReturnStatement (expr));
1021 mainClass.Members.Add (prop);
1024 void CreateApplicationInstance ()
1026 CodeMemberProperty prop = new CodeMemberProperty ();
1027 Type appType = typeof (HttpApplication);
1028 prop.Type = new CodeTypeReference (appType);
1029 prop.Name = "ApplicationInstance";
1030 prop.Attributes = MemberAttributes.Family | MemberAttributes.Final;
1032 CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression (thisRef, "Context");
1034 propRef = new CodePropertyReferenceExpression (propRef, "ApplicationInstance");
1036 CodeCastExpression cast = new CodeCastExpression (appType.FullName, propRef);
1037 prop.GetStatements.Add (new CodeMethodReturnStatement (cast));
1038 mainClass.Members.Add (prop);
1041 void CreateAutoHandlers ()
1043 // Create AutoHandlers property
1044 CodeMemberProperty prop = new CodeMemberProperty ();
1045 prop.Type = new CodeTypeReference (typeof (int));
1046 prop.Name = "AutoHandlers";
1047 prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1049 CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
1050 CodeFieldReferenceExpression fldRef ;
1051 fldRef = new CodeFieldReferenceExpression (mainClassExpr, "__autoHandlers");
1052 ret.Expression = fldRef;
1053 prop.GetStatements.Add (ret);
1055 prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ()));
1057 mainClass.Members.Add (prop);
1059 // Add the __autoHandlers field
1060 CodeMemberField fld = new CodeMemberField (typeof (int), "__autoHandlers");
1061 fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
1062 mainClass.Members.Add (fld);
1065 void CreateAutoEventWireup ()
1067 // The getter returns false
1068 CodeMemberProperty prop = new CodeMemberProperty ();
1069 prop.Type = new CodeTypeReference (typeof (bool));
1070 prop.Name = "SupportAutoEvents";
1071 prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1072 prop.GetStatements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (false)));
1073 mainClass.Members.Add (prop);
1076 CodeExpression GetExpressionFromString (Type type, string str, MemberInfo member)
1078 if (type == typeof (string))
1079 return new CodePrimitiveExpression (str);
1081 if (type == typeof (bool)) {
1082 if (str == null || str == "" || InvariantCompareNoCase (str, "true"))
1083 return new CodePrimitiveExpression (true);
1084 else if (InvariantCompareNoCase (str, "false"))
1085 return new CodePrimitiveExpression (false);
1087 throw new ParseException (currentLocation,
1088 "Value '" + str + "' is not a valid boolean.");
1092 return new CodePrimitiveExpression (null);
1094 if (type.IsPrimitive)
1095 return new CodePrimitiveExpression (Convert.ChangeType (str, type, CultureInfo.InvariantCulture));
1097 if (type == typeof (string [])) {
1098 string [] subs = str.Split (',');
1099 CodeArrayCreateExpression expr = new CodeArrayCreateExpression ();
1100 expr.CreateType = new CodeTypeReference (typeof (string));
1101 foreach (string v in subs) {
1102 expr.Initializers.Add (new CodePrimitiveExpression (v.Trim ()));
1108 if (type == typeof (Color)){
1109 if (colorConverter == null)
1110 colorConverter = TypeDescriptor.GetConverter (typeof (Color));
1112 if (str.Trim().Length == 0) {
1113 CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (typeof (Color));
1114 return new CodeFieldReferenceExpression (ft, "Empty");
1119 if (str.IndexOf (',') == -1) {
1120 c = (Color) colorConverter.ConvertFromString (str);
1122 int [] argb = new int [4];
1125 string [] parts = str.Split (',');
1126 int length = parts.Length;
1128 throw new Exception ();
1130 int basei = (length == 4) ? 0 : 1;
1131 for (int i = length - 1; i >= 0; i--) {
1132 argb [basei + i] = (int) Byte.Parse (parts [i]);
1134 c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]);
1136 } catch (Exception e){
1137 // Hack: "LightGrey" is accepted, but only for ASP.NET, as the
1138 // TypeConverter for Color fails to ConvertFromString.
1139 // Hence this hack...
1140 if (InvariantCompareNoCase ("LightGrey", str)) {
1141 c = Color.LightGray;
1143 throw new ParseException (currentLocation,
1144 "Color " + str + " is not a valid color.", e);
1148 if (c.IsKnownColor){
1149 CodeFieldReferenceExpression expr = new CodeFieldReferenceExpression ();
1150 if (c.IsSystemColor)
1151 type = typeof (SystemColors);
1153 expr.TargetObject = new CodeTypeReferenceExpression (type);
1154 expr.FieldName = c.Name;
1157 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1158 m.TargetObject = new CodeTypeReferenceExpression (type);
1159 m.MethodName = "FromArgb";
1160 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
1161 invoke.Parameters.Add (new CodePrimitiveExpression (c.A));
1162 invoke.Parameters.Add (new CodePrimitiveExpression (c.R));
1163 invoke.Parameters.Add (new CodePrimitiveExpression (c.G));
1164 invoke.Parameters.Add (new CodePrimitiveExpression (c.B));
1169 TypeConverter converter = TypeDescriptor.GetProperties (member.DeclaringType) [member.Name].Converter;
1171 if (converter != null && converter.CanConvertFrom (typeof (string))) {
1172 object value = converter.ConvertFrom (str);
1174 if (converter.CanConvertTo (typeof (InstanceDescriptor))) {
1175 InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor));
1176 return GenerateInstance (idesc, true);
1179 CodeExpression exp = GenerateObjectInstance (value, false);
1180 if (exp != null) return exp;
1182 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1183 m.TargetObject = new CodeTypeReferenceExpression (typeof (TypeDescriptor));
1184 m.MethodName = "GetConverter";
1185 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
1186 CodeTypeReference tref = new CodeTypeReference (type);
1187 invoke.Parameters.Add (new CodeTypeOfExpression (tref));
1189 invoke = new CodeMethodInvokeExpression (invoke, "ConvertFrom");
1190 invoke.Parameters.Add (new CodePrimitiveExpression (str));
1192 return new CodeCastExpression (tref, invoke);
1195 Console.WriteLine ("Unknown type: " + type + " value: " + str);
1197 return new CodePrimitiveExpression (str);
1200 CodeExpression GenerateInstance (InstanceDescriptor idesc, bool throwOnError)
1202 CodeExpression[] parameters = new CodeExpression [idesc.Arguments.Count];
1204 foreach (object ob in idesc.Arguments) {
1205 CodeExpression exp = GenerateObjectInstance (ob, throwOnError);
1206 if (exp == null) return null;
1207 parameters [n++] = exp;
1210 switch (idesc.MemberInfo.MemberType) {
1211 case MemberTypes.Constructor:
1212 CodeTypeReference tob = new CodeTypeReference (idesc.MemberInfo.DeclaringType);
1213 return new CodeObjectCreateExpression (tob, parameters);
1215 case MemberTypes.Method:
1216 CodeTypeReferenceExpression mt = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
1217 return new CodeMethodInvokeExpression (mt, idesc.MemberInfo.Name, parameters);
1219 case MemberTypes.Field:
1220 CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
1221 return new CodeFieldReferenceExpression (ft, idesc.MemberInfo.Name);
1223 case MemberTypes.Property:
1224 CodeTypeReferenceExpression pt = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
1225 return new CodePropertyReferenceExpression (pt, idesc.MemberInfo.Name);
1227 throw new ParseException (currentLocation, "Invalid instance type.");
1230 CodeExpression GenerateObjectInstance (object value, bool throwOnError)
1233 return new CodePrimitiveExpression (null);
1235 Type t = value.GetType();
1236 if (t.IsPrimitive || value is string)
1237 return new CodePrimitiveExpression (value);
1240 Array ar = (Array) value;
1241 CodeExpression[] items = new CodeExpression [ar.Length];
1242 for (int n=0; n<ar.Length; n++) {
1243 CodeExpression exp = GenerateObjectInstance (ar.GetValue (n), throwOnError);
1244 if (exp == null) return null;
1247 return new CodeArrayCreateExpression (new CodeTypeReference (t), items);
1250 TypeConverter converter = TypeDescriptor.GetConverter (t);
1251 if (converter != null && converter.CanConvertTo (typeof (InstanceDescriptor))) {
1252 InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor));
1253 return GenerateInstance (idesc, throwOnError);
1256 InstanceDescriptor desc = GetDefaultInstanceDescriptor (value);
1257 if (desc != null) return GenerateInstance (desc, throwOnError);
1260 throw new ParseException (currentLocation, "Cannot generate an instance for the type: " + t);
1265 InstanceDescriptor GetDefaultInstanceDescriptor (object value)
1267 if (value is System.Web.UI.WebControls.Unit) {
1268 System.Web.UI.WebControls.Unit s = (System.Web.UI.WebControls.Unit) value;
1269 MethodInfo met = typeof(System.Web.UI.WebControls.Unit).GetMethod ("Parse", new Type[] {typeof(string)});
1270 return new InstanceDescriptor (met, new object[] {s.ToString ()});
1273 if (value is System.Web.UI.WebControls.FontUnit) {
1274 System.Web.UI.WebControls.FontUnit s = (System.Web.UI.WebControls.FontUnit) value;
1275 MethodInfo met = typeof(System.Web.UI.WebControls.FontUnit).GetMethod ("Parse", new Type[] {typeof(string)});
1276 return new InstanceDescriptor (met, new object[] {s.ToString ()});