2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / TemplateControlCompiler.cs
1 //
2 // System.Web.Compilation.TemplateControlCompiler
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2003 Ximian, Inc (http://www.ximian.com)
8 //
9
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30 using System;
31 using System.CodeDom;
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Drawing;
35 using System.Globalization;
36 using System.Reflection;
37 using System.Text;
38 using System.Web;
39 using System.Web.UI;
40
41 namespace System.Web.Compilation
42 {
43         class TemplateControlCompiler : BaseCompiler
44         {
45                 static BindingFlags noCaseFlags = BindingFlags.Public | BindingFlags.NonPublic |
46                                                   BindingFlags.Instance | BindingFlags.IgnoreCase;
47
48                 static Type styleType = typeof (System.Web.UI.WebControls.Style);
49                 static Type fontinfoType = typeof (System.Web.UI.WebControls.FontInfo);
50
51                 TemplateControlParser parser;
52                 int dataBoundAtts;
53                 ILocation currentLocation;
54
55                 static TypeConverter colorConverter;
56
57                 static CodeVariableReferenceExpression ctrlVar = new CodeVariableReferenceExpression ("__ctrl");
58                 static Type [] arrayString = new Type [] {typeof (string)};
59                 static Type [] arrayStringCultureInfo = new Type [] {typeof (string), typeof (CultureInfo)};
60
61                 public TemplateControlCompiler (TemplateControlParser parser)
62                         : base (parser)
63                 {
64                         this.parser = parser;
65                 }
66
67                 void EnsureID (ControlBuilder builder)
68                 {
69                         if (builder.ID == null || builder.ID.Trim () == "")
70                                 builder.ID = builder.GetNextID (null);
71                 }
72
73                 void CreateField (ControlBuilder builder, bool check)
74                 {
75                         currentLocation = builder.location;
76                         if (check && CheckBaseFieldOrProperty (builder.ID, builder.ControlType))
77                                 return; // The field or property already exists in a base class and is accesible.
78
79                         CodeMemberField field;
80                         field = new CodeMemberField (builder.ControlType.FullName, builder.ID);
81                         field.Attributes = MemberAttributes.Family;
82                         mainClass.Members.Add (field);
83                 }
84
85                 bool CheckBaseFieldOrProperty (string id, Type type)
86                 {
87                         FieldInfo fld = parser.BaseType.GetField (id, noCaseFlags);
88
89                         Type other = null;
90                         if (fld == null || fld.IsPrivate) {
91                                 PropertyInfo prop = parser.BaseType.GetProperty (id, noCaseFlags);
92                                 if (prop != null) {
93                                         MethodInfo setm = prop.GetSetMethod (true);
94                                         if (setm != null)
95                                                 other = prop.PropertyType;
96                                 }
97                         } else {
98                                 other = fld.FieldType;
99                         }
100                         
101                         if (other == null)
102                                 return false;
103
104                         if (!other.IsAssignableFrom (type)) {
105                                 string msg = String.Format ("The base class includes the field '{0}', but its " +
106                                                             "type '{1}' is not compatible with {2}",
107                                                             id, other, type);
108                                 throw new ParseException (currentLocation, msg);
109                         }
110
111                         return true;
112                 }
113
114                 void AddParsedSubObjectStmt (ControlBuilder builder, CodeExpression expr) 
115                 {
116                         if (!builder.haveParserVariable) {
117                                 CodeVariableDeclarationStatement p = new CodeVariableDeclarationStatement();
118                                 p.Name = "__parser";
119                                 p.Type = new CodeTypeReference (typeof (IParserAccessor));
120                                 p.InitExpression = new CodeCastExpression (typeof (IParserAccessor), ctrlVar);
121                                 builder.method.Statements.Add (p);
122                                 builder.haveParserVariable = true;
123                         }
124
125                         CodeVariableReferenceExpression var = new CodeVariableReferenceExpression ("__parser");
126                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (var, "AddParsedSubObject");
127                         invoke.Parameters.Add (expr);
128                         builder.method.Statements.Add (invoke);
129                 }
130
131                 void InitMethod (ControlBuilder builder, bool isTemplate, bool childrenAsProperties)
132                 {
133                         string tailname = ((builder is RootBuilder) ? "Tree" : ("_" + builder.ID));
134                         CodeMemberMethod method = new CodeMemberMethod ();
135                         builder.method = method;
136                         method.Name = "__BuildControl" + tailname;
137                         method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
138                         Type type = builder.ControlType;
139
140                         if (builder.HasAspCode) {
141                                 CodeMemberMethod renderMethod = new CodeMemberMethod ();
142                                 builder.renderMethod = renderMethod;
143                                 renderMethod.Name = "__Render" + tailname;
144                                 renderMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
145                                 CodeParameterDeclarationExpression arg1 = new CodeParameterDeclarationExpression ();
146                                 arg1.Type = new CodeTypeReference (typeof (HtmlTextWriter));
147                                 arg1.Name = "__output";
148                                 CodeParameterDeclarationExpression arg2 = new CodeParameterDeclarationExpression ();
149                                 arg2.Type = new CodeTypeReference (typeof (Control));
150                                 arg2.Name = "parameterContainer";
151                                 renderMethod.Parameters.Add (arg1);
152                                 renderMethod.Parameters.Add (arg2);
153                                 mainClass.Members.Add (renderMethod);
154                         }
155                         
156                         if (childrenAsProperties || builder.ControlType == null) {
157                                 string typeString;
158                                 if (builder.ControlType != null && builder.isProperty &&
159                                     !typeof (ITemplate).IsAssignableFrom (builder.ControlType))
160                                         typeString = builder.ControlType.FullName;
161                                 else 
162                                         typeString = "System.Web.UI.Control";
163
164                                 method.Parameters.Add (new CodeParameterDeclarationExpression (typeString, "__ctrl"));
165                         } else {
166                                 
167                                 if (typeof (Control).IsAssignableFrom (type))
168                                         method.ReturnType = new CodeTypeReference (typeof (Control));
169
170                                 CodeObjectCreateExpression newExpr = new CodeObjectCreateExpression (type);
171
172                                 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
173                                 if (atts != null && atts.Length > 0) {
174                                         ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
175                                         if (att.NeedsTag)
176                                                 newExpr.Parameters.Add (new CodePrimitiveExpression (builder.TagName));
177                                 } else if (builder is DataBindingBuilder) {
178                                         newExpr.Parameters.Add (new CodePrimitiveExpression (0));
179                                         newExpr.Parameters.Add (new CodePrimitiveExpression (1));
180                                 }
181
182                                 method.Statements.Add (new CodeVariableDeclarationStatement (builder.ControlType, "__ctrl"));
183                                 CodeAssignStatement assign = new CodeAssignStatement ();
184                                 assign.Left = ctrlVar;
185                                 assign.Right = newExpr;
186                                 method.Statements.Add (assign);
187                                 
188                                 CodeFieldReferenceExpression builderID = new CodeFieldReferenceExpression ();
189                                 builderID.TargetObject = thisRef;
190                                 builderID.FieldName = builder.ID;
191                                 assign = new CodeAssignStatement ();
192                                 assign.Left = builderID;
193                                 assign.Right = ctrlVar;
194                                 method.Statements.Add (assign);
195                                 if (typeof (UserControl).IsAssignableFrom (type)) {
196                                         CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression ();
197                                         mref.TargetObject = builderID;
198                                         mref.MethodName = "InitializeAsUserControl";
199                                         CodeMethodInvokeExpression initAsControl = new CodeMethodInvokeExpression (mref);
200                                         initAsControl.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page"));
201                                         method.Statements.Add (initAsControl);
202                                 }
203 #if NET_2_0
204                                 if (typeof (System.Web.UI.WebControls.ContentPlaceHolder).IsAssignableFrom (type)) {
205                                         CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders");
206                                         CodeMethodInvokeExpression addPlaceholder = new CodeMethodInvokeExpression (prop, "Add");
207                                         addPlaceholder.Parameters.Add (ctrlVar);
208                                         method.Statements.Add (addPlaceholder);
209                                 }
210 #endif
211                         }
212
213                         mainClass.Members.Add (method);
214                 }
215
216                 void AddLiteralSubObject (ControlBuilder builder, string str)
217                 {
218                         if (!builder.HasAspCode) {
219                                 CodeObjectCreateExpression expr;
220                                 expr = new CodeObjectCreateExpression (typeof (LiteralControl), new CodePrimitiveExpression (str));
221                                 AddParsedSubObjectStmt (builder, expr);
222                         } else {
223                                 CodeMethodReferenceExpression methodRef = new CodeMethodReferenceExpression ();
224                                 methodRef.TargetObject = new CodeArgumentReferenceExpression ("__output");
225                                 methodRef.MethodName = "Write";
226
227                                 CodeMethodInvokeExpression expr;
228                                 expr = new CodeMethodInvokeExpression (methodRef, new CodePrimitiveExpression (str));
229                                 builder.renderMethod.Statements.Add (expr);
230                         }
231                 }
232
233                 string TrimDB (string value)
234                 {
235                         string str = value.Trim ();
236                         str = str.Substring (3);
237                         return str.Substring (0, str.Length - 2);
238                 }
239
240                 string DataBoundProperty (ControlBuilder builder, Type type, string varName, string value)
241                 {
242                         value = TrimDB (value);
243                         CodeMemberMethod method;
244                         string dbMethodName = builder.method.Name + "_DB_" + dataBoundAtts++;
245
246                         method = CreateDBMethod (dbMethodName, GetContainerType (builder), builder.ControlType);
247
248                         CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
249
250                         // This should be a CodePropertyReferenceExpression for properties... but it works anyway
251                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (targetExpr, varName);
252
253                         CodeExpression expr;
254                         if (type == typeof (string)) {
255                                 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
256                                 CodeTypeReferenceExpression conv = new CodeTypeReferenceExpression (typeof (Convert));
257                                 tostring.Method = new CodeMethodReferenceExpression (conv, "ToString");
258                                 tostring.Parameters.Add (new CodeSnippetExpression (value));
259                                 expr = tostring;
260                         } else {
261                                 CodeSnippetExpression snippet = new CodeSnippetExpression (value);
262                                 expr = new CodeCastExpression (type, snippet);
263                         }
264                         
265                         method.Statements.Add (new CodeAssignStatement (field, expr));
266                         mainClass.Members.Add (method);
267                         return method.Name;
268                 }
269
270                 void AddCodeForPropertyOrField (ControlBuilder builder, Type type, string var_name, string att, bool isDataBound)
271                 {
272                         CodeMemberMethod method = builder.method;
273                         if (isDataBound) {
274                                 string dbMethodName = DataBoundProperty (builder, type, var_name, att);
275                                 AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName);
276                                 return;
277                         }
278
279                         CodeAssignStatement assign = new CodeAssignStatement ();
280                         assign.Left = new CodePropertyReferenceExpression (ctrlVar, var_name);
281                         currentLocation = builder.location;
282                         assign.Right = GetExpressionFromString (type, att);
283
284                         method.Statements.Add (assign);
285                 }
286
287                 bool IsDataBound (string value)
288                 {
289                         if (value == null || value == "")
290                                 return false;
291
292                         string str = value.Trim ();
293                         return (str.StartsWith ("<%#") && str.EndsWith ("%>"));
294                 }
295                 
296                 bool ProcessPropertiesAndFields (ControlBuilder builder, MemberInfo member, string id, string attValue)
297                 {
298                         int hyphen = id.IndexOf ('-');
299
300                         bool isPropertyInfo = (member is PropertyInfo);
301                         bool is_processed = false;
302                         bool isDataBound = IsDataBound (attValue);
303
304                         Type type;
305                         if (isPropertyInfo) {
306                                 type = ((PropertyInfo) member).PropertyType;
307                                 if (hyphen == -1 && ((PropertyInfo) member).CanWrite == false)
308                                         return false;
309                         } else {
310                                 type = ((FieldInfo) member).FieldType;
311                         }
312
313                         if (0 == String.Compare (member.Name, id, true)){
314                                 AddCodeForPropertyOrField (builder, type, member.Name, attValue, isDataBound);
315                                 return true;
316                         }
317                         
318                         if (hyphen == -1)
319                                 return false;
320
321                         string prop_field = id.Replace ("-", ".");
322                         string [] parts = prop_field.Split (new char [] {'.'});
323                         if (parts.Length != 2 || 0 != String.Compare (member.Name, parts [0], true))
324                                 return false;
325
326                         PropertyInfo [] subprops = type.GetProperties ();
327                         foreach (PropertyInfo subprop in subprops) {
328                                 if (0 != String.Compare (subprop.Name, parts [1], true))
329                                         continue;
330
331                                 if (subprop.CanWrite == false)
332                                         return false;
333
334                                 bool is_bool = subprop.PropertyType == typeof (bool);
335                                 if (!is_bool && attValue == null)
336                                         return false; // Font-Size -> Font-Size="" as html
337
338                                 string value;
339                                 if (attValue == null && is_bool)
340                                         value = "true"; // Font-Bold <=> Font-Bold="true"
341                                 else
342                                         value = attValue;
343
344                                 AddCodeForPropertyOrField (builder, subprop.PropertyType,
345                                                  member.Name + "." + subprop.Name,
346                                                  value, isDataBound);
347                                 is_processed = true;
348                         }
349
350                         return is_processed;
351                 }
352
353                 void AddEventAssign (CodeMemberMethod method, string name, Type type, string value)
354                 {
355                         //"__ctrl.{0} += new {1} (this.{2});"
356                         CodeEventReferenceExpression evtID = new CodeEventReferenceExpression (ctrlVar, name);
357
358                         CodeDelegateCreateExpression create;
359                         create = new CodeDelegateCreateExpression (new CodeTypeReference (type), thisRef, value);
360
361                         CodeAttachEventStatement attach = new CodeAttachEventStatement (evtID, create);
362                         method.Statements.Add (attach);
363                 }
364                 
365                 void CreateAssignStatementsFromAttributes (ControlBuilder builder)
366                 {
367                         this.dataBoundAtts = 0;
368                         IDictionary atts = builder.attribs;
369                         if (atts == null || atts.Count == 0)
370                                 return;
371
372                         EventInfo [] ev_info = null;
373                         PropertyInfo [] prop_info = null;
374                         FieldInfo [] field_info = null;
375                         bool is_processed = false;
376                         Type type = builder.ControlType;
377
378                         foreach (string id in atts.Keys){
379                                 if (0 == String.Compare (id, "runat", true))
380                                         continue;
381
382                                 is_processed = false;
383                                 string attvalue = atts [id] as string;
384                                 if (id.Length > 2 && id.Substring (0, 2).ToUpper () == "ON"){
385                                         if (ev_info == null)
386                                                 ev_info = type.GetEvents ();
387
388                                         string id_as_event = id.Substring (2);
389                                         foreach (EventInfo ev in ev_info){
390                                                 if (0 == String.Compare (ev.Name, id_as_event, true)){
391                                                         AddEventAssign (builder.method,
392                                                                         ev.Name,
393                                                                         ev.EventHandlerType,
394                                                                         attvalue);
395
396                                                         is_processed = true;
397                                                         break;
398                                                 }
399                                         }
400
401                                         if (is_processed)
402                                                 continue;
403                                 } 
404
405                                 if (prop_info == null)
406                                         prop_info = type.GetProperties ();
407
408                                 foreach (PropertyInfo prop in prop_info) {
409                                         is_processed = ProcessPropertiesAndFields (builder, prop, id, attvalue);
410                                         if (is_processed)
411                                                 break;
412                                 }
413
414                                 if (is_processed)
415                                         continue;
416
417                                 if (field_info == null)
418                                         field_info = type.GetFields ();
419
420                                 foreach (FieldInfo field in field_info){
421                                         is_processed = ProcessPropertiesAndFields (builder, field, id, attvalue);
422                                         if (is_processed)
423                                                 break;
424                                 }
425
426                                 if (is_processed)
427                                         continue;
428
429                                 if (!typeof (IAttributeAccessor).IsAssignableFrom (type))
430                                         throw new ParseException (builder.location, "Unrecognized attribute: " + id);
431
432
433                                 CodeCastExpression cast = new CodeCastExpression (typeof (IAttributeAccessor), ctrlVar);
434                                 CodeMethodReferenceExpression methodExpr;
435                                 methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
436                                 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (methodExpr);
437                                 expr.Parameters.Add (new CodePrimitiveExpression (id));
438                                 expr.Parameters.Add (new CodePrimitiveExpression ((string) atts [id]));
439                                 builder.method.Statements.Add (expr);
440                         }
441                 }
442
443                 void AddRenderControl (ControlBuilder builder)
444                 {
445                         CodeIndexerExpression indexer = new CodeIndexerExpression ();
446                         indexer.TargetObject = new CodePropertyReferenceExpression (
447                                                         new CodeArgumentReferenceExpression ("parameterContainer"),
448                                                         "Controls");
449                                                         
450                         indexer.Indices.Add (new CodePrimitiveExpression (builder.renderIndex));
451                         
452                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (indexer, "RenderControl");
453                         invoke.Parameters.Add (new CodeArgumentReferenceExpression ("__output"));
454                         builder.renderMethod.Statements.Add (invoke);
455                         builder.renderIndex++;
456                 }
457
458                 void AddChildCall (ControlBuilder parent, ControlBuilder child)
459                 {
460                         CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.method.Name);
461                         CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (m);
462
463                         object [] atts = child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true);
464                         if (atts != null && atts.Length > 0) {
465                                 PartialCachingAttribute pca = (PartialCachingAttribute) atts [0];
466                                 CodeTypeReferenceExpression cc = new CodeTypeReferenceExpression("System.Web.UI.StaticPartialCachingControl");
467                                 CodeMethodInvokeExpression build = new CodeMethodInvokeExpression (cc, "BuildCachedControl");
468                                 build.Parameters.Add (new CodeArgumentReferenceExpression("__ctrl"));
469                                 build.Parameters.Add (new CodePrimitiveExpression (child.ID));
470 #if NET_1_1
471                                 if (pca.Shared)
472                                         build.Parameters.Add (new CodePrimitiveExpression (child.ControlType.GetHashCode ().ToString ()));
473                                 else
474 #endif
475                                         build.Parameters.Add (new CodePrimitiveExpression (Guid.NewGuid ().ToString ()));
476                                         
477                                 build.Parameters.Add (new CodePrimitiveExpression (pca.Duration));
478                                 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByParams));
479                                 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByControls));
480                                 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByCustom));
481                                 build.Parameters.Add (new CodeDelegateCreateExpression (
482                                                               new CodeTypeReference (typeof (System.Web.UI.BuildMethod)),
483                                                               thisRef, child.method.Name));
484                                 
485                                 parent.method.Statements.Add (build);
486                                 if (parent.HasAspCode)
487                                         AddRenderControl (parent);
488                                 return;
489                         }
490                                 
491                         if (child.isProperty || parent.ChildrenAsProperties) {
492                                 expr.Parameters.Add (new CodeFieldReferenceExpression (ctrlVar, child.TagName));
493                                 parent.method.Statements.Add (expr);
494                                 return;
495                         }
496
497                         parent.method.Statements.Add (expr);
498                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, child.ID);
499                         if (parent.ControlType == null || typeof (IParserAccessor).IsAssignableFrom (parent.ControlType)) {
500                                 AddParsedSubObjectStmt (parent, field);
501                         } else {
502                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (ctrlVar, "Add");
503                                 invoke.Parameters.Add (field);
504                                 parent.method.Statements.Add (invoke);
505                         }
506                                 
507                         if (parent.HasAspCode)
508                                 AddRenderControl (parent);
509                 }
510
511                 void AddTemplateInvocation (CodeMemberMethod method, string name, string methodName)
512                 {
513                         CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
514
515                         CodeObjectCreateExpression newBuild = new CodeObjectCreateExpression (typeof (BuildTemplateMethod));
516                         newBuild.Parameters.Add (new CodeMethodReferenceExpression (thisRef, methodName));
517
518                         CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
519                         newCompiled.Parameters.Add (newBuild);
520
521                         CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
522                         method.Statements.Add (assign);
523                 }
524
525 #if NET_2_0
526                 void AddContentTemplateInvocation (ContentControlBuilder cbuilder, CodeMemberMethod method, string methodName)
527                 {
528                         CodePropertyReferenceExpression pag = new CodePropertyReferenceExpression (ctrlVar, "Page");
529                         CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (pag, "Master");
530
531                         CodeObjectCreateExpression newBuild = new CodeObjectCreateExpression (typeof (BuildTemplateMethod));
532                         newBuild.Parameters.Add (new CodeMethodReferenceExpression (thisRef, methodName));
533
534                         CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
535                         newCompiled.Parameters.Add (newBuild);
536                         
537                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (prop, "AddContentTemplate");
538                         invoke.Parameters.Add (new CodePrimitiveExpression (cbuilder.ContentPlaceHolderID));
539                         invoke.Parameters.Add (newCompiled);
540
541                         method.Statements.Add (invoke);
542                 }
543 #endif
544
545                 void AddCodeRender (ControlBuilder parent, CodeRenderBuilder cr)
546                 {
547                         if (cr.Code == null || cr.Code.Trim () == "")
548                                 return;
549
550                         if (!cr.IsAssign) {
551                                 CodeSnippetStatement code = new CodeSnippetStatement (cr.Code);
552                                 parent.renderMethod.Statements.Add (code);
553                                 return;
554                         }
555
556                         CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression ();
557                         expr.Method = new CodeMethodReferenceExpression (
558                                                         new CodeArgumentReferenceExpression ("__output"),
559                                                         "Write");
560
561                         expr.Parameters.Add (new CodeSnippetExpression (cr.Code));
562                         parent.renderMethod.Statements.Add (expr);
563                 }
564
565                 static Type GetContainerType (ControlBuilder builder)
566                 {
567                         TemplateBuilder tb = builder as TemplateBuilder;
568                         if (tb != null && tb.ContainerType != null)
569                                 return tb.ContainerType;
570
571                         Type type = builder.NamingContainerType;
572
573                         PropertyInfo prop = type.GetProperty ("Items", noCaseFlags);
574                         if (prop == null)
575                                 return type;
576
577                         Type ptype = prop.PropertyType;
578                         if (!typeof (ICollection).IsAssignableFrom (ptype))
579                                 return type;
580
581                         prop = ptype.GetProperty ("Item", noCaseFlags);
582                         if (prop == null)
583                                 return type;
584
585                         return prop.PropertyType;
586                 }
587                 
588                 CodeMemberMethod CreateDBMethod (string name, Type container, Type target)
589                 {
590                         CodeMemberMethod method = new CodeMemberMethod ();
591                         method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
592                         method.Name = name;
593                         method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "sender"));
594                         method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (EventArgs), "e"));
595
596                         CodeTypeReference containerRef = new CodeTypeReference (container);
597                         CodeTypeReference targetRef = new CodeTypeReference (target);
598
599                         CodeVariableDeclarationStatement decl = new CodeVariableDeclarationStatement();
600                         decl.Name = "Container";
601                         decl.Type = containerRef;
602                         method.Statements.Add (decl);
603
604                         decl = new CodeVariableDeclarationStatement();
605                         decl.Name = "target";
606                         decl.Type = targetRef;
607                         method.Statements.Add (decl);
608
609                         CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
610                         CodeAssignStatement assign = new CodeAssignStatement ();
611                         assign.Left = targetExpr;
612                         assign.Right = new CodeCastExpression (targetRef, new CodeArgumentReferenceExpression ("sender"));
613                         method.Statements.Add (assign);
614
615                         assign = new CodeAssignStatement ();
616                         assign.Left = new CodeVariableReferenceExpression ("Container");
617                         assign.Right = new CodeCastExpression (containerRef,
618                                                 new CodePropertyReferenceExpression (targetExpr, "BindingContainer"));
619                         method.Statements.Add (assign);
620
621                         return method;
622                 }
623
624                 void AddDataBindingLiteral (ControlBuilder builder, DataBindingBuilder db)
625                 {
626                         if (db.Code == null || db.Code.Trim () == "")
627                                 return;
628
629                         EnsureID (db);
630                         CreateField (db, false);
631
632                         string dbMethodName = "__DataBind_" + db.ID;
633                         // Add the method that builds the DataBoundLiteralControl
634                         InitMethod (db, false, false);
635                         CodeMemberMethod method = db.method;
636                         AddEventAssign (method, "DataBinding", typeof (EventHandler), dbMethodName);
637                         method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
638
639                         // Add the DataBind handler
640                         method = CreateDBMethod (dbMethodName, GetContainerType (builder), typeof (DataBoundLiteralControl));
641
642                         CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
643                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
644                         invoke.Method = new CodeMethodReferenceExpression (targetExpr, "SetDataBoundString");
645                         invoke.Parameters.Add (new CodePrimitiveExpression (0));
646
647                         CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
648                         tostring.Method = new CodeMethodReferenceExpression (
649                                                         new CodeTypeReferenceExpression (typeof (Convert)),
650                                                         "ToString");
651                         tostring.Parameters.Add (new CodeSnippetExpression (db.Code));
652                         invoke.Parameters.Add (tostring);
653                         method.Statements.Add (invoke);
654                         
655                         mainClass.Members.Add (method);
656
657                         AddChildCall (builder, db);
658                 }
659
660                 void FlushText (ControlBuilder builder, StringBuilder sb)
661                 {
662                         if (sb.Length > 0) {
663                                 AddLiteralSubObject (builder, sb.ToString ());
664                                 sb.Length = 0;
665                         }
666                 }
667
668                 void CreateControlTree (ControlBuilder builder, bool inTemplate, bool childrenAsProperties)
669                 {
670                         EnsureID (builder);
671                         bool isTemplate = (typeof (TemplateBuilder).IsAssignableFrom (builder.GetType ()));
672                         if (!isTemplate && !inTemplate) {
673                                 CreateField (builder, true);
674                         } else if (!isTemplate) {
675                                 builder.ID = builder.GetNextID (null);
676                                 CreateField (builder, false);
677                         }
678
679                         InitMethod (builder, isTemplate, childrenAsProperties);
680                         if (!isTemplate || builder.GetType () == typeof (RootBuilder))
681                                 CreateAssignStatementsFromAttributes (builder);
682
683                         if (builder.Children != null && builder.Children.Count > 0) {
684                                 ArrayList templates = null;
685
686                                 StringBuilder sb = new StringBuilder ();
687                                 foreach (object b in builder.Children) {
688
689                                         if (b is string) {
690                                                 sb.Append ((string) b);
691                                                 continue;
692                                         }
693
694                                         FlushText (builder, sb);
695                                         if (b is ObjectTagBuilder) {
696                                                 ProcessObjectTag ((ObjectTagBuilder) b);
697                                                 continue;
698                                         }
699
700 #if NET_2_0
701                                         if (b is ContentControlBuilder) {
702                                                 ContentControlBuilder cb = (ContentControlBuilder) b;
703                                                 CreateControlTree (cb, true, true);
704                                                 AddContentTemplateInvocation (cb, builder.method, cb.method.Name);
705                                                 continue;
706                                         }
707 #endif
708
709                                         if (b is TemplateBuilder) {
710                                                 if (templates == null)
711                                                         templates = new ArrayList ();
712
713                                                 templates.Add (b);
714                                                 continue;
715                                         }
716
717                                         if (b is CodeRenderBuilder) {
718                                                 AddCodeRender (builder, (CodeRenderBuilder) b);
719                                                 continue;
720                                         }
721
722                                         if (b is DataBindingBuilder) {
723                                                 AddDataBindingLiteral (builder, (DataBindingBuilder) b);
724                                                 continue;
725                                         }
726                                         
727                                         if (b is ControlBuilder) {
728                                                 ControlBuilder child = (ControlBuilder) b;
729                                                 CreateControlTree (child, inTemplate, builder.ChildrenAsProperties);
730                                                 AddChildCall (builder, child);
731                                                 continue;
732                                         }
733
734                                         throw new Exception ("???");
735                                 }
736
737                                 FlushText (builder, sb);
738
739                                 if (templates != null) {
740                                         foreach (ControlBuilder b in templates) {
741                                                 CreateControlTree (b, true, false);
742                                                 AddTemplateInvocation (builder.method, b.TagName, b.method.Name);
743                                         }
744                                 }
745
746                         }
747
748                         if (builder.defaultPropertyBuilder != null) {
749                                 ControlBuilder b = builder.defaultPropertyBuilder;
750                                 CreateControlTree (b, false, true);
751                                 AddChildCall (builder, b);
752                         }
753
754                         if (builder.HasAspCode) {
755                                 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
756                                 m.TargetObject = thisRef;
757                                 m.MethodName = builder.renderMethod.Name;
758
759                                 CodeDelegateCreateExpression create = new CodeDelegateCreateExpression ();
760                                 create.DelegateType = new CodeTypeReference (typeof (RenderMethod));
761                                 create.TargetObject = thisRef;
762                                 create.MethodName = builder.renderMethod.Name;
763
764                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
765                                 invoke.Method = new CodeMethodReferenceExpression (ctrlVar, "SetRenderMethodDelegate");
766                                 invoke.Parameters.Add (create);
767
768                                 builder.method.Statements.Add (invoke);
769                         }
770                         
771                         if (!childrenAsProperties && typeof (Control).IsAssignableFrom (builder.ControlType))
772                                 builder.method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
773                 }
774
775                 protected override void CreateMethods ()
776                 {
777                         base.CreateMethods ();
778
779                         CreateProperties ();
780                         CreateControlTree (parser.RootBuilder, false, false);
781                         CreateFrameworkInitializeMethod ();
782                 }
783
784                 void CreateFrameworkInitializeMethod ()
785                 {
786                         CodeMemberMethod method = new CodeMemberMethod ();
787                         method.Name = "FrameworkInitialize";
788                         method.Attributes = MemberAttributes.Family | MemberAttributes.Override;
789                         AddStatementsToFrameworkInitialize (method);
790                         mainClass.Members.Add (method);
791                 }
792
793                 protected virtual void AddStatementsToFrameworkInitialize (CodeMemberMethod method)
794                 {
795                         if (!parser.EnableViewState) {
796                                 CodeAssignStatement stmt = new CodeAssignStatement ();
797                                 stmt.Left = new CodePropertyReferenceExpression (thisRef, "EnableViewState");
798                                 stmt.Right = new CodePrimitiveExpression (false);
799                                 method.Statements.Add (stmt);
800                         }
801
802                         CodeMethodReferenceExpression methodExpr;
803                         methodExpr = new CodeMethodReferenceExpression (thisRef, "__BuildControlTree");
804                         CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (methodExpr, thisRef);
805                         method.Statements.Add (new CodeExpressionStatement (expr));
806                 }
807
808                 protected override void AddApplicationAndSessionObjects ()
809                 {
810                         foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.ApplicationObjects) {
811                                 CreateFieldForObject (tag.Type, tag.ObjectID);
812                                 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, true, false);
813                         }
814
815                         foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.SessionObjects) {
816                                 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, false, false);
817                         }
818                 }
819
820                 protected void ProcessObjectTag (ObjectTagBuilder tag)
821                 {
822                         string fieldName = CreateFieldForObject (tag.Type, tag.ObjectID);
823                         CreatePropertyForObject (tag.Type, tag.ObjectID, fieldName, false);
824                 }
825
826                 void CreateProperties ()
827                 {
828                         if (!parser.AutoEventWireup) {
829                                 CreateAutoEventWireup ();
830                         } else {
831                                 CreateAutoHandlers ();
832                         }
833
834                         CreateApplicationInstance ();
835                         CreateTemplateSourceDirectory ();
836                 }
837
838                 void CreateTemplateSourceDirectory ()
839                 {
840                         CodeMemberProperty prop = new CodeMemberProperty ();
841                         prop.Type = new CodeTypeReference (typeof (string));
842                         prop.Name = "TemplateSourceDirectory";
843                         prop.Attributes = MemberAttributes.Public | MemberAttributes.Override;
844
845                         CodePrimitiveExpression expr = new CodePrimitiveExpression (parser.BaseVirtualDir);
846                         prop.GetStatements.Add (new CodeMethodReturnStatement (expr));
847                         mainClass.Members.Add (prop);
848                 }
849
850                 void CreateApplicationInstance ()
851                 {
852                         CodeMemberProperty prop = new CodeMemberProperty ();
853                         Type appType = typeof (HttpApplication);
854                         prop.Type = new CodeTypeReference (appType);
855                         prop.Name = "ApplicationInstance";
856                         prop.Attributes = MemberAttributes.Family | MemberAttributes.Final;
857
858                         CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression (thisRef, "Context");
859
860                         propRef = new CodePropertyReferenceExpression (propRef, "ApplicationInstance");
861
862                         CodeCastExpression cast = new CodeCastExpression (appType.FullName, propRef);
863                         prop.GetStatements.Add (new CodeMethodReturnStatement (cast));
864                         mainClass.Members.Add (prop);
865                 }
866
867                 void CreateAutoHandlers ()
868                 {
869                         // Create AutoHandlers property
870                         CodeMemberProperty prop = new CodeMemberProperty ();
871                         prop.Type = new CodeTypeReference (typeof (int));
872                         prop.Name = "AutoHandlers";
873                         prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
874                         
875                         CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
876                         CodeFieldReferenceExpression fldRef ;
877                         fldRef = new CodeFieldReferenceExpression (mainClassExpr, "__autoHandlers");
878                         ret.Expression = fldRef;
879                         prop.GetStatements.Add (ret);
880                         
881                         prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ()));
882
883                         mainClass.Members.Add (prop);
884
885                         // Add the __autoHandlers field
886                         CodeMemberField fld = new CodeMemberField (typeof (int), "__autoHandlers");
887                         fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
888                         mainClass.Members.Add (fld);
889                 }
890
891                 void CreateAutoEventWireup ()
892                 {
893                         // The getter returns false
894                         CodeMemberProperty prop = new CodeMemberProperty ();
895                         prop.Type = new CodeTypeReference (typeof (bool));
896                         prop.Name = "SupportAutoEvents";
897                         prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
898                         prop.GetStatements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (false)));
899                         mainClass.Members.Add (prop);
900                 }
901                 
902                 CodeExpression GetExpressionFromString (Type type, string str)
903                 {
904                         if (type == typeof (string))
905                                 return new CodePrimitiveExpression (str);
906
907                         if (type == typeof (bool)) {
908                                 if (str == null || str == "" || 0 == String.Compare (str, "true", true))
909                                         return new CodePrimitiveExpression (true);
910                                 else if (0 == String.Compare (str, "false", true))
911                                         return new CodePrimitiveExpression (false);
912                                 else
913                                         throw new ParseException (currentLocation,
914                                                         "Value '" + str  + "' is not a valid boolean.");
915                         }
916
917                         if (str == null)
918                                 return new CodePrimitiveExpression (null);
919
920                         if (type.IsPrimitive)
921                                 return new CodePrimitiveExpression (Convert.ChangeType (str, type));
922
923                         if (type.IsEnum) {
924                                 object val = null;
925                                 try {
926                                         val = Enum.Parse (type, str, true);
927                                 } catch (Exception) {
928                                         throw new ParseException (currentLocation,
929                                                         str + " is not a valid value for enum '" + type + "'");
930                                 }
931                                 CodeFieldReferenceExpression expr = new CodeFieldReferenceExpression ();
932                                 expr.TargetObject = new CodeTypeReferenceExpression (type);
933                                 expr.FieldName = val.ToString ();
934                                 return expr;
935                         }
936
937                         if (type == typeof (string [])) {
938                                 string [] subs = str.Split (',');
939                                 CodeArrayCreateExpression expr = new CodeArrayCreateExpression ();
940                                 expr.CreateType = new CodeTypeReference (typeof (string));
941                                 foreach (string v in subs) {
942                                         expr.Initializers.Add (new CodePrimitiveExpression (v.Trim ()));
943                                 }
944
945                                 return expr;
946                         }
947
948                         if (type == typeof (Size)) {
949                                 string [] subs = str.Split (',');
950                                 if (subs.Length != 2)
951                                         throw new ParseException (currentLocation,
952                                                 String.Format ("Cannot create {0} from '{1}'", type, str));
953
954                                 int width = 0;
955                                 int height = 0;
956                                 try {
957                                         width = Int32.Parse (subs [0]);
958                                         height = Int32.Parse (subs [0]);
959                                         new Size (width, height);
960                                 } catch {
961                                         throw new ParseException (currentLocation,
962                                                 String.Format ("Cannot create {0} from '{1}'", type, str));
963                                 }
964                                 
965                                 CodeObjectCreateExpression expr = new CodeObjectCreateExpression ();
966                                 expr.CreateType = new CodeTypeReference (type);
967                                 expr.Parameters.Add (new CodePrimitiveExpression (width));
968                                 expr.Parameters.Add (new CodePrimitiveExpression (height));
969                                 return expr;
970                         }
971
972                         if (type == typeof (Color)){
973                                 if (colorConverter == null)
974                                         colorConverter = TypeDescriptor.GetConverter (typeof (Color));
975
976                                 Color c;
977                                 try {
978                                         if (str.IndexOf (',') == -1) {
979                                                 c = (Color) colorConverter.ConvertFromString (str);
980                                         } else {
981                                                 int [] argb = new int [4];
982                                                 argb [0] = 255;
983
984                                                 string [] parts = str.Split (',');
985                                                 int length = parts.Length;
986                                                 if (length < 3)
987                                                         throw new Exception ();
988
989                                                 int basei = (length == 4) ? 0 : 1;
990                                                 for (int i = length - 1; i >= 0; i--) {
991                                                         argb [basei + i] = (int) Byte.Parse (parts [i]);
992                                                 }
993                                                 c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]);
994                                         }
995                                 } catch (Exception e){
996                                         throw new ParseException (currentLocation,
997                                                         "Color " + str + " is not a valid color.", e);
998                                 }
999
1000                                 if (c.IsKnownColor){
1001                                         CodeFieldReferenceExpression expr = new CodeFieldReferenceExpression ();
1002                                         if (c.IsSystemColor)
1003                                                 type = typeof (SystemColors);
1004
1005                                         expr.TargetObject = new CodeTypeReferenceExpression (type);
1006                                         expr.FieldName = c.Name;
1007                                         return expr;
1008                                 } else {
1009                                         CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1010                                         m.TargetObject = new CodeTypeReferenceExpression (type);
1011                                         m.MethodName = "FromArgb";
1012                                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
1013                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.A));
1014                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.R));
1015                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.G));
1016                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.B));
1017                                         return invoke;
1018                                 }
1019                         }
1020
1021                         TypeConverter converter = TypeDescriptor.GetConverter (type);
1022                         if (converter != null && converter.CanConvertFrom (typeof (string))) {
1023                                 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1024                                 m.TargetObject = new CodeTypeReferenceExpression (typeof (TypeDescriptor));
1025                                 m.MethodName = "GetConverter";
1026                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
1027                                 CodeTypeReference tref = new CodeTypeReference (type);
1028                                 invoke.Parameters.Add (new CodeTypeOfExpression (tref));
1029                                 
1030                                 invoke = new CodeMethodInvokeExpression (invoke, "ConvertFrom");
1031                                 invoke.Parameters.Add (new CodePrimitiveExpression (str));
1032
1033                                 return new CodeCastExpression (tref, invoke);
1034                         }
1035
1036                         bool parms = false;
1037                         BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
1038                         MethodInfo parse = type.GetMethod ("Parse", flags, null, arrayStringCultureInfo, null);
1039                         if (parse != null) {
1040                                 parms = true;
1041                         } else {
1042                                 parse = type.GetMethod ("Parse", flags, null, arrayString, null);
1043                         }
1044
1045                         if (parse != null) {
1046                                 object o = null;
1047                                 try {
1048                                         if (parms)
1049                                                 o = parse.Invoke (null, new object [] { str, CultureInfo.InvariantCulture });
1050                                         else
1051                                                 o = parse.Invoke (null, new object [] { str });
1052                                 } catch (Exception e) {
1053                                         throw new ParseException (currentLocation, "Cannot parse " + str + " as " + type, e);
1054                                 }
1055                                 
1056                                 if (o == null)
1057                                         throw new ParseException (currentLocation, str + " as " + type + " is null");
1058
1059                                 CodeTypeReferenceExpression exprType = new CodeTypeReferenceExpression (type);
1060                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (exprType, "Parse");
1061                                 //FIXME: may be we gotta ensure roundtrip between o.ToString and Parse
1062                                 invoke.Parameters.Add (new CodePrimitiveExpression (o.ToString ()));
1063                                 if (parms) {
1064                                         CodeTypeReferenceExpression texp = new CodeTypeReferenceExpression (typeof (CultureInfo));
1065                                         CodePropertyReferenceExpression pexp = new CodePropertyReferenceExpression ();
1066                                         pexp.TargetObject = texp;
1067                                         pexp.PropertyName = "InvariantCulture";
1068                                         invoke.Parameters.Add (pexp);
1069                                 }
1070                                 return invoke;
1071                         }
1072
1073                         // FIXME: Arrays
1074                         Console.WriteLine ("Unknown type: " + type + " value: " + str);
1075
1076                         return new CodePrimitiveExpression (str);
1077                 }
1078         }
1079 }
1080