2008-09-01 Marek Habersack <mhabersack@novell.com>
[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 //      Marek Habersack (mhabersack@novell.com)
7 //
8 // (C) 2003 Ximian, Inc (http://www.ximian.com)
9 // (C) 2004-2008 Novell, Inc (http://novell.com)
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 using System;
32 using System.CodeDom;
33 using System.Collections;
34 using System.ComponentModel;
35 using System.Drawing;
36 using System.Globalization;
37 using System.Reflection;
38 using System.Text;
39 using System.Web;
40 using System.Web.UI;
41 using System.Web.UI.WebControls;
42 using System.Web.Util;
43 using System.ComponentModel.Design.Serialization;
44 using System.Text.RegularExpressions;
45 #if NET_2_0
46 using System.Configuration;
47 using System.Collections.Specialized;
48 using System.Collections.Generic;
49 using System.Web.Configuration;
50 #endif
51
52 namespace System.Web.Compilation
53 {
54         class TemplateControlCompiler : BaseCompiler
55         {
56                 static BindingFlags noCaseFlags = BindingFlags.Public | BindingFlags.NonPublic |
57                                                   BindingFlags.Instance | BindingFlags.IgnoreCase;
58                 static Type monoTypeType = Type.GetType ("System.MonoType");
59                 
60                 TemplateControlParser parser;
61                 int dataBoundAtts;
62                 internal ILocation currentLocation;
63
64                 static TypeConverter colorConverter;
65
66                 internal static CodeVariableReferenceExpression ctrlVar = new CodeVariableReferenceExpression ("__ctrl");
67                 
68 #if NET_2_0
69                 static Regex bindRegex = new Regex (@"Bind\s*\([""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\)\s*%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
70                 static Regex bindRegexInValue = new Regex (@"Bind\s*\([""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
71 #endif
72                 static Regex evalRegexInValue = new Regex (@"Eval\s*\([""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
73                 
74                 public TemplateControlCompiler (TemplateControlParser parser)
75                         : base (parser)
76                 {
77                         this.parser = parser;
78                 }
79
80                 protected void EnsureID (ControlBuilder builder)
81                 {
82                         if (builder.ID == null || builder.ID.Trim () == "")
83                                 builder.ID = builder.GetNextID (null);
84                 }
85
86                 void CreateField (ControlBuilder builder, bool check)
87                 {
88                         if (builder == null || builder.ID == null || builder.ControlType == null)
89                                 return;
90 #if NET_2_0
91                         if (partialNameOverride [builder.ID] != null)
92                                 return;
93 #endif
94
95                         MemberAttributes ma = MemberAttributes.Family;
96                         currentLocation = builder.location;
97                         if (check && CheckBaseFieldOrProperty (builder.ID, builder.ControlType, ref ma))
98                                 return; // The field or property already exists in a base class and is accesible.
99
100                         CodeMemberField field;
101                         field = new CodeMemberField (builder.ControlType.FullName, builder.ID);
102                         field.Attributes = ma;
103 #if NET_2_0
104                         field.Type.Options |= CodeTypeReferenceOptions.GlobalReference;
105
106                         if (partialClass != null)
107                                 partialClass.Members.Add (AddLinePragma (field, builder));
108                         else
109 #endif
110                                 mainClass.Members.Add (AddLinePragma (field, builder));
111                 }
112
113                 bool CheckBaseFieldOrProperty (string id, Type type, ref MemberAttributes ma)
114                 {
115                         FieldInfo fld = parser.BaseType.GetField (id, noCaseFlags);
116
117                         Type other = null;
118                         if (fld == null || fld.IsPrivate) {
119                                 PropertyInfo prop = parser.BaseType.GetProperty (id, noCaseFlags);
120                                 if (prop != null) {
121                                         MethodInfo setm = prop.GetSetMethod (true);
122                                         if (setm != null)
123                                                 other = prop.PropertyType;
124                                 }
125                         } else {
126                                 other = fld.FieldType;
127                         }
128                         
129                         if (other == null)
130                                 return false;
131
132                         if (!other.IsAssignableFrom (type)) {
133 #if NET_2_0
134                                 ma |= MemberAttributes.New;
135                                 return false;
136 #else
137                                 string msg = String.Format ("The base class includes the field '{0}', but its " +
138                                                             "type '{1}' is not compatible with {2}",
139                                                             id, other, type);
140                                 throw new ParseException (currentLocation, msg);
141 #endif
142                         }
143
144                         return true;
145                 }
146                 
147                 void AddParsedSubObjectStmt (ControlBuilder builder, CodeExpression expr) 
148                 {
149                         if (!builder.haveParserVariable) {
150                                 CodeVariableDeclarationStatement p = new CodeVariableDeclarationStatement();
151                                 p.Name = "__parser";
152                                 p.Type = new CodeTypeReference (typeof (IParserAccessor));
153                                 p.InitExpression = new CodeCastExpression (typeof (IParserAccessor), ctrlVar);
154                                 builder.methodStatements.Add (p);
155                                 builder.haveParserVariable = true;
156                         }
157
158                         CodeVariableReferenceExpression var = new CodeVariableReferenceExpression ("__parser");
159                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (var, "AddParsedSubObject");
160                         invoke.Parameters.Add (expr);
161                         builder.methodStatements.Add (AddLinePragma (invoke, builder));
162                 }
163                 
164                 void InitMethod (ControlBuilder builder, bool isTemplate, bool childrenAsProperties)
165                 {
166                         currentLocation = builder.location;
167                         
168                         string tailname = ((builder is RootBuilder) ? "Tree" : ("_" + builder.ID));
169                         CodeMemberMethod method = new CodeMemberMethod ();
170                         builder.method = method;
171                         builder.methodStatements = method.Statements;
172
173                         method.Name = "__BuildControl" + tailname;
174                         method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
175                         Type type = builder.ControlType;
176
177                         /* in the case this is the __BuildControlTree
178                          * method, allow subclasses to insert control
179                          * specific code. */
180                         if (builder is RootBuilder) {
181 #if NET_2_0
182                                 SetCustomAttributes (method);
183 #endif
184                                 AddStatementsToInitMethod (method);
185                         }
186                         
187                         if (builder.HasAspCode) {
188                                 CodeMemberMethod renderMethod = new CodeMemberMethod ();
189                                 builder.renderMethod = renderMethod;
190                                 renderMethod.Name = "__Render" + tailname;
191                                 renderMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
192                                 CodeParameterDeclarationExpression arg1 = new CodeParameterDeclarationExpression ();
193                                 arg1.Type = new CodeTypeReference (typeof (HtmlTextWriter));
194                                 arg1.Name = "__output";
195                                 CodeParameterDeclarationExpression arg2 = new CodeParameterDeclarationExpression ();
196                                 arg2.Type = new CodeTypeReference (typeof (Control));
197                                 arg2.Name = "parameterContainer";
198                                 renderMethod.Parameters.Add (arg1);
199                                 renderMethod.Parameters.Add (arg2);
200                                 mainClass.Members.Add (renderMethod);
201                         }
202                         
203                         if (childrenAsProperties || builder.ControlType == null) {
204                                 string typeString;
205                                 if (builder is RootBuilder)
206                                         typeString = parser.ClassName;
207                                 else {
208                                         if (builder.ControlType != null && builder.isProperty &&
209                                             !typeof (ITemplate).IsAssignableFrom (builder.ControlType))
210                                                 typeString = builder.ControlType.FullName;
211                                         else 
212                                                 typeString = "System.Web.UI.Control";
213                                 }
214
215                                 method.Parameters.Add (new CodeParameterDeclarationExpression (typeString, "__ctrl"));
216                         } else {
217                                 
218                                 if (typeof (Control).IsAssignableFrom (type))
219                                         method.ReturnType = new CodeTypeReference (typeof (Control));
220
221                                 // _ctrl = new $controlType ($parameters);
222                                 //
223                                 CodeObjectCreateExpression newExpr = new CodeObjectCreateExpression (type);
224
225                                 object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
226                                 if (atts != null && atts.Length > 0) {
227                                         ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
228                                         if (att.NeedsTag)
229                                                 newExpr.Parameters.Add (new CodePrimitiveExpression (builder.TagName));
230                                 } else if (builder is DataBindingBuilder) {
231                                         newExpr.Parameters.Add (new CodePrimitiveExpression (0));
232                                         newExpr.Parameters.Add (new CodePrimitiveExpression (1));
233                                 }
234
235                                 method.Statements.Add (new CodeVariableDeclarationStatement (builder.ControlType, "__ctrl"));
236                                 CodeAssignStatement assign = new CodeAssignStatement ();
237                                 assign.Left = ctrlVar;
238                                 assign.Right = newExpr;
239                                 method.Statements.Add (AddLinePragma (assign, builder));
240                                                                 
241                                 // this.$builderID = _ctrl;
242                                 //
243                                 CodeFieldReferenceExpression builderID = new CodeFieldReferenceExpression ();
244                                 builderID.TargetObject = thisRef;
245                                 builderID.FieldName = builder.ID;
246                                 assign = new CodeAssignStatement ();
247                                 assign.Left = builderID;
248                                 assign.Right = ctrlVar;
249                                 method.Statements.Add (AddLinePragma (assign, builder));
250
251                                 if (typeof (UserControl).IsAssignableFrom (type)) {
252                                         CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression ();
253                                         mref.TargetObject = builderID;
254                                         mref.MethodName = "InitializeAsUserControl";
255                                         CodeMethodInvokeExpression initAsControl = new CodeMethodInvokeExpression (mref);
256                                         initAsControl.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page"));
257                                         method.Statements.Add (initAsControl);
258                                 }
259
260 #if NET_2_0
261                                 if (builder.ParentTemplateBuilder is System.Web.UI.WebControls.ContentBuilderInternal) {
262                                         PropertyInfo pi;
263
264                                         try {
265                                                 pi = type.GetProperty ("TemplateControl");
266                                         } catch (Exception) {
267                                                 pi = null;
268                                         }
269
270                                         if (pi != null && pi.CanWrite) {
271                                                 // __ctrl.TemplateControl = this;
272                                                 assign = new CodeAssignStatement ();
273                                                 assign.Left = new CodePropertyReferenceExpression (ctrlVar, "TemplateControl");;
274                                                 assign.Right = thisRef;
275                                                 method.Statements.Add (assign);
276                                         }
277                                 }
278                                 
279                                 // _ctrl.SkinID = $value
280                                 // _ctrl.ApplyStyleSheetSkin (this);
281                                 //
282                                 // the SkinID assignment needs to come
283                                 // before the call to
284                                 // ApplyStyleSheetSkin, for obvious
285                                 // reasons.  We skip SkinID in
286                                 // CreateAssignStatementsFromAttributes
287                                 // below.
288                                 // 
289                                 if (builder.attribs != null) {
290                                         string skinid = builder.attribs ["skinid"] as string;
291                                         if (skinid != null)
292                                                 CreateAssignStatementFromAttribute (builder, "skinid");
293                                 }
294                                 if (typeof (WebControl).IsAssignableFrom (type)) {
295                                         CodeMethodInvokeExpression applyStyleSheetSkin = new CodeMethodInvokeExpression (ctrlVar, "ApplyStyleSheetSkin");
296                                         if (typeof (Page).IsAssignableFrom (parser.BaseType))
297                                                 applyStyleSheetSkin.Parameters.Add (thisRef);
298                                         else
299                                                 applyStyleSheetSkin.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page"));
300                                         method.Statements.Add (applyStyleSheetSkin);
301                                 }
302 #endif
303
304                                 // Process template children before anything else
305                                 ArrayList templates = builder.TemplateChildren;
306                                 if (templates != null && templates.Count > 0) {
307                                         foreach (TemplateBuilder tb in templates) {
308                                                 CreateControlTree (tb, true, false);
309 #if NET_2_0
310                                                 if (tb.BindingDirection == BindingDirection.TwoWay) {
311                                                         string extractMethod = CreateExtractValuesMethod (tb);
312                                                         AddBindableTemplateInvocation (builder, tb.TagName, tb.method.Name, extractMethod);
313                                                 } else
314 #endif
315                                                         AddTemplateInvocation (builder, tb.TagName, tb.method.Name);
316                                         }
317                                 }
318
319                                 // process ID here. It should be set before any other attributes are
320                                 // assigned, since the control code may rely on ID being set. We
321                                 // skip ID in CreateAssignStatementsFromAttributes
322                                 if (builder.attribs != null) {
323                                         string ctl_id = builder.attribs ["id"] as string;
324                                         if (ctl_id != null && ctl_id != String.Empty)
325                                                 CreateAssignStatementFromAttribute (builder, "id");
326                                 }
327                                 
328 #if NET_2_0
329                                 if (typeof (ContentPlaceHolder).IsAssignableFrom (type)) {
330                                         CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders");
331                                         CodeMethodInvokeExpression addPlaceholder = new CodeMethodInvokeExpression (prop, "Add");
332                                         addPlaceholder.Parameters.Add (ctrlVar);
333                                         method.Statements.Add (addPlaceholder);
334
335
336                                         CodeConditionStatement condStatement;
337
338                                         // Add the __Template_* field
339                                         CodeMemberField fld = new CodeMemberField (typeof (ITemplate), "__Template_" + builder.ID);
340                                         fld.Attributes = MemberAttributes.Private;
341                                         mainClass.Members.Add (fld);
342
343                                         CodeFieldReferenceExpression templateID = new CodeFieldReferenceExpression ();
344                                         templateID.TargetObject = thisRef;
345                                         templateID.FieldName = "__Template_" + builder.ID;
346
347                                         // if ((this.ContentTemplates != null)) {
348                                         //      this.__Template_$builder.ID = ((System.Web.UI.ITemplate)(this.ContentTemplates["$builder.ID"]));
349                                         // }
350                                         //
351                                         CodeFieldReferenceExpression contentTemplates = new CodeFieldReferenceExpression ();
352                                         contentTemplates.TargetObject = thisRef;
353                                         contentTemplates.FieldName = "ContentTemplates";
354
355                                         CodeIndexerExpression indexer = new CodeIndexerExpression ();
356                                         indexer.TargetObject = new CodePropertyReferenceExpression (thisRef, "ContentTemplates");
357                                         indexer.Indices.Add (new CodePrimitiveExpression (builder.ID));
358
359                                         assign = new CodeAssignStatement ();
360                                         assign.Left = templateID;
361                                         assign.Right = new CodeCastExpression (new CodeTypeReference (typeof (ITemplate)), indexer);
362
363                                         condStatement = new CodeConditionStatement (new CodeBinaryOperatorExpression (contentTemplates,
364                                                                                                                       CodeBinaryOperatorType.IdentityInequality,
365                                                                                                                       new CodePrimitiveExpression (null)),
366                                                                                     assign);
367
368                                         method.Statements.Add (condStatement);
369
370                                         // if ((this.__Template_mainContent != null)) {
371                                         //      this.__Template_mainContent.InstantiateIn(__ctrl);
372                                         // }
373                                         // and also set things up such that any additional code ends up in:
374                                         // else {
375                                         //      ...
376                                         // }
377                                         //
378                                         CodeMethodReferenceExpression methodRef = new CodeMethodReferenceExpression ();
379                                         methodRef.TargetObject = templateID;
380                                         methodRef.MethodName = "InstantiateIn";
381
382                                         CodeMethodInvokeExpression instantiateInInvoke;
383                                         instantiateInInvoke = new CodeMethodInvokeExpression (methodRef, ctrlVar);
384
385                                         condStatement = new CodeConditionStatement (new CodeBinaryOperatorExpression (templateID,
386                                                                                                                       CodeBinaryOperatorType.IdentityInequality,
387                                                                                                                       new CodePrimitiveExpression (null)),
388                                                                                     new CodeExpressionStatement (instantiateInInvoke));
389                                         method.Statements.Add (condStatement);
390
391                                         // this is the bit that causes the following stuff to end up in the else { }
392                                         builder.methodStatements = condStatement.FalseStatements;
393                                 }
394 #endif
395                         }
396                         
397                         mainClass.Members.Add (method);
398                 }
399
400 #if NET_2_0
401                 void SetCustomAttribute (CodeMemberMethod method, UnknownAttributeDescriptor uad)
402                 {
403                         CodeAssignStatement assign = new CodeAssignStatement ();
404                         assign.Left = new CodePropertyReferenceExpression (
405                                 new CodeArgumentReferenceExpression("__ctrl"),
406                                 uad.Info.Name);
407                         assign.Right = GetExpressionFromString (uad.Value.GetType (), uad.Value.ToString (), uad.Info);
408                         
409                         method.Statements.Add (assign);
410                 }
411                 
412                 void SetCustomAttributes (CodeMemberMethod method)
413                 {
414                         Type baseType = parser.BaseType;
415                         if (baseType == null)
416                                 return;
417                         
418                         List <UnknownAttributeDescriptor> attrs = parser.UnknownMainAttributes;
419                         if (attrs == null || attrs.Count == 0)
420                                 return;
421
422                         foreach (UnknownAttributeDescriptor uad in attrs)
423                                 SetCustomAttribute (method, uad);
424                 }
425 #endif
426                 
427                 protected virtual void AddStatementsToInitMethod (CodeMemberMethod method)
428                 {
429                 }
430
431                 void AddLiteralSubObject (ControlBuilder builder, string str)
432                 {
433                         if (!builder.HasAspCode) {
434                                 CodeObjectCreateExpression expr;
435                                 expr = new CodeObjectCreateExpression (typeof (LiteralControl), new CodePrimitiveExpression (str));
436                                 AddParsedSubObjectStmt (builder, expr);
437                         } else {
438                                 CodeMethodReferenceExpression methodRef = new CodeMethodReferenceExpression ();
439                                 methodRef.TargetObject = new CodeArgumentReferenceExpression ("__output");
440                                 methodRef.MethodName = "Write";
441
442                                 CodeMethodInvokeExpression expr;
443                                 expr = new CodeMethodInvokeExpression (methodRef, new CodePrimitiveExpression (str));
444                                 builder.renderMethod.Statements.Add (AddLinePragma (expr, builder));
445                         }
446                 }
447
448                 string TrimDB (string value, bool trimTail)
449                 {
450                         string str = value.Trim ();
451                         int len = str.Length;
452                         int idx = str.IndexOf ('#', 2) + 1;
453                         if (idx >= len)
454                                 return String.Empty;
455                         if (trimTail)
456                                 len -= 2;
457                         
458                         return str.Substring (idx, len - idx).Trim ();
459                 }
460
461                 CodeMethodInvokeExpression CreateEvalInvokeExpression (Regex regex, string value, int first, int second)
462                 {
463                         Match match = regex.Match (value);
464                         if (!match.Success)
465                                 return null;
466
467                         Group secondGroup = match.Groups [second];
468                         CodeMethodInvokeExpression ret = new CodeMethodInvokeExpression ();
469
470                         ret.Method = new CodeMethodReferenceExpression (thisRef, "Eval");
471                         ret.Parameters.Add (new CodePrimitiveExpression (match.Groups [first].Value));
472
473                         if (secondGroup.Success)
474                                 ret.Parameters.Add (new CodePrimitiveExpression (secondGroup.Value));
475
476                         return ret;
477                 }
478
479                 string DataBoundProperty (ControlBuilder builder, Type type, string varName, string value)
480                 {
481                         value = TrimDB (value, true);
482                         CodeMemberMethod method;
483                         string dbMethodName = builder.method.Name + "_DB_" + dataBoundAtts++;
484                         CodeExpression valueExpression = null;
485                         value = value.Trim ();
486                         
487 #if NET_2_0
488                         bool need_if = false;
489                         if (StrUtils.StartsWith (value, "Bind", true)) {
490                                 valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, 1, 4);
491                                 if (valueExpression != null)
492                                         need_if = true;
493                         } else
494 #endif
495                         if (StrUtils.StartsWith (value, "Eval", true))
496                                 valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, 1, 4);
497                         
498                         if (valueExpression == null)
499                                 valueExpression = new CodeSnippetExpression (value);
500                         
501                         method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType);
502                         
503                         CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
504
505                         // This should be a CodePropertyReferenceExpression for properties... but it works anyway
506                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (targetExpr, varName);
507
508                         CodeExpression expr;
509                         if (type == typeof (string)) {
510                                 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
511                                 CodeTypeReferenceExpression conv = new CodeTypeReferenceExpression (typeof (Convert));
512                                 tostring.Method = new CodeMethodReferenceExpression (conv, "ToString");
513                                 tostring.Parameters.Add (valueExpression);
514                                 expr = tostring;
515                         } else
516                                 expr = new CodeCastExpression (type, valueExpression);
517
518                         CodeAssignStatement assign = new CodeAssignStatement (field, expr);
519 #if NET_2_0
520                         if (need_if) {
521                                 CodeExpression page = new CodePropertyReferenceExpression (thisRef, "Page");
522                                 CodeExpression left = new CodeMethodInvokeExpression (page, "GetDataItem");
523                                 CodeBinaryOperatorExpression ce = new CodeBinaryOperatorExpression (left, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
524                                 CodeConditionStatement ccs = new CodeConditionStatement (ce, assign);
525                                 method.Statements.Add (ccs);
526                         }
527                         else
528 #endif
529                                 method.Statements.Add (assign);
530
531                         mainClass.Members.Add (method);
532                         return method.Name;
533                 }
534
535                 void AddCodeForPropertyOrField (ControlBuilder builder, Type type, string var_name, string att, MemberInfo member, bool isDataBound, bool isExpression)
536                 {
537                         CodeMemberMethod method = builder.method;
538                         bool isWritable = IsWritablePropertyOrField (member);
539                         
540                         if (isDataBound && isWritable) {
541                                 string dbMethodName = DataBoundProperty (builder, type, var_name, att);
542                                 AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
543                                 return;
544                         }
545 #if NET_2_0
546                         else if (isExpression && isWritable) {
547                                 AddExpressionAssign (method, builder, member, type, var_name, att);
548                                 return;
549                         }
550 #endif
551
552                         CodeAssignStatement assign = new CodeAssignStatement ();
553                         assign.Left = new CodePropertyReferenceExpression (ctrlVar, var_name);
554                         currentLocation = builder.location;
555                         assign.Right = GetExpressionFromString (type, att, member);
556
557                         method.Statements.Add (AddLinePragma (assign, builder));
558                 }
559
560                 bool IsDirective (string value, char directiveChar)
561                 {
562                         if (value == null || value == String.Empty)
563                                 return false;
564                         
565                         value = value.Trim ();
566                         if (!StrUtils.StartsWith (value, "<%") || !StrUtils.EndsWith (value, "%>"))
567                                 return false;
568
569                         int dcIndex = value.IndexOf (directiveChar, 2);
570                         if (dcIndex == -1)
571                                 return false;
572
573                         if (dcIndex == 2)
574                                 return true;
575                         dcIndex--;
576                         
577                         while (dcIndex >= 2) {
578                                 if (!Char.IsWhiteSpace (value [dcIndex]))
579                                         return false;
580                                 dcIndex--;
581                         }
582
583                         return true;
584                 }
585                 
586                 bool IsDataBound (string value)
587                 {
588                         return IsDirective (value, '#');
589                 }
590
591 #if NET_2_0
592                 bool IsExpression (string value)
593                 {
594                         return IsDirective (value, '$');
595                 }               
596
597                 void RegisterBindingInfo (ControlBuilder builder, string propName, ref string value)
598                 {
599                         string str = TrimDB (value, false);
600                         if (StrUtils.StartsWith (str, "Bind", true)) {
601                                 Match match = bindRegex.Match (str);
602                                 if (match.Success) {
603                                         string bindingName = match.Groups [1].Value;
604                                         TemplateBuilder templateBuilder = builder.ParentTemplateBuilder;
605                                         if (templateBuilder == null || templateBuilder.BindingDirection == BindingDirection.OneWay)
606                                                 throw new HttpException ("Bind expression not allowed in this context.");
607                                                 
608                                         string id = builder.attribs ["ID"] as string;
609                                         if (id == null)
610                                                 throw new HttpException ("Control of type '" + builder.ControlType + "' using two-way binding on property '" + propName + "' must have an ID.");
611                                         
612                                         templateBuilder.RegisterBoundProperty (builder.ControlType, propName, id, bindingName);
613                                 }
614                         }
615                 }
616 #endif
617
618                 /*
619                 static bool InvariantCompare (string a, string b)
620                 {
621                         return (0 == String.Compare (a, b, false, CultureInfo.InvariantCulture));
622                 }
623                 */
624
625                 static bool InvariantCompareNoCase (string a, string b)
626                 {
627                         return (0 == String.Compare (a, b, true, CultureInfo.InvariantCulture));
628                 }
629
630                 static MemberInfo GetFieldOrProperty (Type type, string name)
631                 {
632                         MemberInfo member = null;
633                         try {
634                                 member = type.GetProperty (name, noCaseFlags & ~BindingFlags.NonPublic);
635                         } catch {}
636                         
637                         if (member != null)
638                                 return member;
639
640                         try {
641                                 member = type.GetField (name, noCaseFlags & ~BindingFlags.NonPublic);
642                         } catch {}
643
644                         return member;
645                 }
646
647                 static bool IsWritablePropertyOrField (MemberInfo member)
648                 {
649                         PropertyInfo pi = member as PropertyInfo;
650                         if (pi != null)
651                                 return pi.CanWrite;
652                         FieldInfo fi = member as FieldInfo;
653                         if (fi != null)
654                                 return !fi.IsInitOnly;
655                         throw new ArgumentException ("Argument must be of PropertyInfo or FieldInfo type", "member");
656                 }
657
658                 bool ProcessPropertiesAndFields (ControlBuilder builder, MemberInfo member, string id,
659                                                  string attValue, string prefix)
660                 {
661                         int hyphen = id.IndexOf ('-');
662                         bool isPropertyInfo = (member is PropertyInfo);
663                         bool isDataBound = IsDataBound (attValue);
664 #if NET_2_0
665                         bool isExpression = !isDataBound && IsExpression (attValue);
666 #else
667                         bool isExpression = false;
668 #endif
669                         Type type;
670                         if (isPropertyInfo) {
671                                 type = ((PropertyInfo) member).PropertyType;
672                         } else {
673                                 type = ((FieldInfo) member).FieldType;
674                         }
675
676                         if (InvariantCompareNoCase (member.Name, id)) {
677 #if NET_2_0
678                                 if (isDataBound)
679                                         RegisterBindingInfo (builder, member.Name, ref attValue);
680                                 
681 #endif
682                                 if (!IsWritablePropertyOrField (member))
683                                         return false;
684                                 
685                                 AddCodeForPropertyOrField (builder, type, member.Name, attValue, member, isDataBound, isExpression);
686                                 return true;
687                         }
688                         
689                         if (hyphen == -1)
690                                 return false;
691
692                         string prop_field = id.Replace ("-", ".");
693                         string [] parts = prop_field.Split (new char [] {'.'});
694                         int length = parts.Length;
695                         
696                         if (length < 2 || !InvariantCompareNoCase (member.Name, parts [0]))
697                                 return false;
698
699                         if (length > 2) {
700                                 MemberInfo sub_member = GetFieldOrProperty (type, parts [1]);
701                                 if (sub_member == null)
702                                         return false;
703
704                                 string new_prefix = prefix + member.Name + ".";
705                                 string new_id = id.Substring (hyphen + 1);
706                                 return ProcessPropertiesAndFields (builder, sub_member, new_id, attValue, new_prefix);
707                         }
708
709                         MemberInfo subpf = GetFieldOrProperty (type, parts [1]);
710                         if (!(subpf is PropertyInfo))
711                                 return false;
712
713                         PropertyInfo subprop = (PropertyInfo) subpf;
714                         if (subprop.CanWrite == false)
715                                 return false;
716
717                         bool is_bool = (subprop.PropertyType == typeof (bool));
718                         if (!is_bool && attValue == null)
719                                 return false; // Font-Size -> Font-Size="" as html
720
721                         string val = attValue;
722                         if (attValue == null && is_bool)
723                                 val = "true"; // Font-Bold <=> Font-Bold="true"
724 #if NET_2_0
725                         if (isDataBound)
726                                 RegisterBindingInfo (builder, prefix + member.Name + "." + subprop.Name, ref attValue);
727 #endif
728                         AddCodeForPropertyOrField (builder, subprop.PropertyType,
729                                                    prefix + member.Name + "." + subprop.Name,
730                                                    val, subprop, isDataBound, isExpression);
731
732                         return true;
733                 }
734
735 #if NET_2_0
736                 CodeExpression CompileExpression (MemberInfo member, Type type, string value, bool useSetAttribute)
737                 {
738                         // First let's find the correct expression builder
739                         value = value.Substring (3, value.Length - 5).Trim ();
740                         int colon = value.IndexOf (':');
741                         if (colon == -1)
742                                 return null;
743                         string prefix = value.Substring (0, colon).Trim ();
744                         string expr = value.Substring (colon + 1).Trim ();
745                         
746                         System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration ("");
747                         if (config == null)
748                                 return null;
749                         
750                         CompilationSection cs = (CompilationSection)config.GetSection ("system.web/compilation");
751                         if (cs == null)
752                                 return null;
753                         
754                         if (cs.ExpressionBuilders == null || cs.ExpressionBuilders.Count == 0)
755                                 return null;
756
757                         System.Web.Configuration.ExpressionBuilder ceb = cs.ExpressionBuilders[prefix];
758                         if (ceb == null)
759                                 return null;
760                         
761                         string builderType = ceb.Type;
762                         Type t;
763                         
764                         try {
765                                 t = HttpApplication.LoadType (builderType, true);
766                         } catch (Exception e) {
767                                 throw new HttpException (String.Format ("Failed to load expression builder type `{0}'", builderType), e);
768                         }
769
770                         if (!typeof (System.Web.Compilation.ExpressionBuilder).IsAssignableFrom (t))
771                                 throw new HttpException (String.Format ("Type {0} is not descendant from System.Web.Compilation.ExpressionBuilder", builderType));
772
773                         System.Web.Compilation.ExpressionBuilder eb = null;
774                         object parsedData;
775                         ExpressionBuilderContext ctx;
776                         
777                         try {
778                                 eb = Activator.CreateInstance (t) as System.Web.Compilation.ExpressionBuilder;
779                                 ctx = new ExpressionBuilderContext (HttpContext.Current.Request.FilePath);
780                                 parsedData = eb.ParseExpression (expr, type, ctx);
781                         } catch (Exception e) {
782                                 throw new HttpException (String.Format ("Failed to create an instance of type `{0}'", builderType), e);
783                         }
784                         
785                         BoundPropertyEntry bpe = CreateBoundPropertyEntry (member as PropertyInfo, prefix, expr, useSetAttribute);
786                         return eb.GetCodeExpression (bpe, parsedData, ctx);
787                 }
788                 
789                 void AddExpressionAssign (CodeMemberMethod method, ControlBuilder builder, MemberInfo member, Type type, string name, string value)
790                 {
791                         CodeExpression expr = CompileExpression (member, type, value, false);
792
793                         if (expr == null)
794                                 return;
795                         
796                         CodeAssignStatement assign = new CodeAssignStatement ();
797                         assign.Left = new CodePropertyReferenceExpression (ctrlVar, name);
798                         assign.Right = expr;
799                         
800                         builder.method.Statements.Add (AddLinePragma (assign, builder));
801                 }
802
803                 BoundPropertyEntry CreateBoundPropertyEntry (PropertyInfo pi, string prefix, string expr, bool useSetAttribute)
804                 {
805                         BoundPropertyEntry ret = new BoundPropertyEntry ();
806                         ret.Expression = expr;
807                         ret.ExpressionPrefix = prefix;
808                         ret.Generated = false;
809                         if (pi != null) {
810                                 ret.Name = pi.Name;
811                                 ret.PropertyInfo = pi;
812                                 ret.Type = pi.PropertyType;
813                         }
814                         ret.UseSetAttribute = useSetAttribute;
815                         
816                         return ret;
817                 }
818                 
819                 void AssignPropertyFromResources (ControlBuilder builder, MemberInfo mi, string attvalue)
820                 {
821                         bool isProperty = mi.MemberType == MemberTypes.Property;
822                         bool isField = !isProperty && (mi.MemberType == MemberTypes.Field);
823
824                         if (!isProperty && !isField || !IsWritablePropertyOrField (mi))
825                                 return;                 
826
827                         string memberName = mi.Name;
828                         string resname = String.Concat (attvalue, ".", memberName);
829
830                         // __ctrl.Text = System.Convert.ToString(HttpContext.GetLocalResourceObject("ButtonResource1.Text"));
831                         string inputFile = parser.InputFile;
832                         string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
833         
834                         if (StrUtils.StartsWith (inputFile, physPath))
835                                 inputFile = parser.InputFile.Substring (physPath.Length - 1);
836                         else
837                                 return;
838
839                         char dsc = System.IO.Path.DirectorySeparatorChar;
840                         if (dsc != '/')
841                                 inputFile = inputFile.Replace (dsc, '/');
842
843                         object obj = HttpContext.GetLocalResourceObject (inputFile, resname);
844                         if (obj == null)
845                                 return;
846
847                         if (!isProperty && !isField)
848                                 return; // an "impossible" case
849                         
850                         CodeAssignStatement assign = new CodeAssignStatement ();
851                         
852                         assign.Left = new CodePropertyReferenceExpression (ctrlVar, memberName);
853                         assign.Right = ResourceExpressionBuilder.CreateGetLocalResourceObject (mi, resname);
854                         
855                         builder.method.Statements.Add (AddLinePragma (assign, builder));
856                 }
857
858                 void AssignPropertiesFromResources (ControlBuilder builder, Type controlType, string attvalue)
859                 {
860                         // Process all public fields and properties of the control. We don't use GetMembers to make the code
861                         // faster
862                         FieldInfo [] fields = controlType.GetFields (
863                                 BindingFlags.Instance | BindingFlags.Static |
864                                 BindingFlags.Public | BindingFlags.FlattenHierarchy);
865                         PropertyInfo [] properties = controlType.GetProperties (
866                                 BindingFlags.Instance | BindingFlags.Static |
867                                 BindingFlags.Public | BindingFlags.FlattenHierarchy);
868
869                         foreach (FieldInfo fi in fields)
870                                 AssignPropertyFromResources (builder, fi, attvalue);
871                         foreach (PropertyInfo pi in properties)
872                                 AssignPropertyFromResources (builder, pi, attvalue);
873                 }
874                 
875                 void AssignPropertiesFromResources (ControlBuilder builder, string attvalue)
876                 {
877                         if (attvalue == null || attvalue.Length == 0)
878                                 return;
879                         
880                         Type controlType = builder.ControlType;
881                         if (controlType == null)
882                                 return;
883
884                         AssignPropertiesFromResources (builder, controlType, attvalue);
885                 }
886 #endif
887                 
888                 void AddEventAssign (CodeMemberMethod method, ControlBuilder builder, string name, Type type, string value)
889                 {
890                         //"__ctrl.{0} += new {1} (this.{2});"
891                         CodeEventReferenceExpression evtID = new CodeEventReferenceExpression (ctrlVar, name);
892
893                         CodeDelegateCreateExpression create;
894                         create = new CodeDelegateCreateExpression (new CodeTypeReference (type), thisRef, value);
895
896                         CodeAttachEventStatement attach = new CodeAttachEventStatement (evtID, create);
897                         method.Statements.Add (attach);
898                 }
899                 
900                 void CreateAssignStatementFromAttribute (ControlBuilder builder, string id)
901                 {
902                         EventInfo [] ev_info = null;
903                         Type type = builder.ControlType;
904                         
905                         string attvalue = builder.attribs [id] as string;
906                         if (id.Length > 2 && id.Substring (0, 2).ToUpper () == "ON"){
907                                 if (ev_info == null)
908                                         ev_info = type.GetEvents ();
909
910                                 string id_as_event = id.Substring (2);
911                                 foreach (EventInfo ev in ev_info){
912                                         if (InvariantCompareNoCase (ev.Name, id_as_event)){
913                                                 AddEventAssign (builder.method,
914                                                                 builder,
915                                                                 ev.Name,
916                                                                 ev.EventHandlerType,
917                                                                 attvalue);
918                                                 
919                                                 return;
920                                         }
921                                 }
922
923                         }
924
925 #if NET_2_0
926                         if (id.ToLower () == "meta:resourcekey") {
927                                 AssignPropertiesFromResources (builder, attvalue);
928                                 return;
929                         }
930 #endif
931                         
932                         int hyphen = id.IndexOf ('-');
933                         string alt_id = id;
934                         if (hyphen != -1)
935                                 alt_id = id.Substring (0, hyphen);
936
937                         MemberInfo fop = GetFieldOrProperty (type, alt_id);
938                         if (fop != null) {
939                                 if (ProcessPropertiesAndFields (builder, fop, id, attvalue, null))
940                                         return;
941                         }
942
943                         if (!typeof (IAttributeAccessor).IsAssignableFrom (type))
944                                 throw new ParseException (builder.location, "Unrecognized attribute: " + id);
945
946                         CodeMemberMethod method = builder.method;
947                         bool isDatabound = IsDataBound (attvalue);
948 #if NET_2_0
949                         bool isExpression = !isDatabound && IsExpression (attvalue);
950 #endif
951
952                         if (isDatabound) {
953                                 string value = attvalue.Substring (3, attvalue.Length - 5).Trim ();
954                                 CodeExpression valueExpression = null;
955 #if NET_2_0
956                                 if (StrUtils.StartsWith (value, "Bind", true))
957                                         valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, 1, 4);
958                                 else
959 #endif
960                                 if (StrUtils.StartsWith (value, "Eval", true))
961                                         valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, 1, 4);
962                                 
963                                 if (valueExpression == null && value != null && value.Trim () != String.Empty)
964                                         valueExpression = new CodeSnippetExpression (value);
965                                 
966                                 CreateDBAttributeMethod (builder, id, valueExpression);
967                         } else {
968                                 CodeCastExpression cast;
969                                 CodeMethodReferenceExpression methodExpr;
970                                 CodeMethodInvokeExpression expr;
971
972                                 cast = new CodeCastExpression (typeof (IAttributeAccessor), ctrlVar);
973                                 methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
974                                 expr = new CodeMethodInvokeExpression (methodExpr);
975                                 expr.Parameters.Add (new CodePrimitiveExpression (id));
976
977                                 CodeExpression valueExpr = null;
978 #if NET_2_0
979                                 if (isExpression)
980                                         valueExpr = CompileExpression (null, typeof (string), attvalue, true);
981
982                                 if (valueExpr == null)
983 #endif
984                                         valueExpr = new CodePrimitiveExpression (attvalue);
985                                 
986                                 expr.Parameters.Add (valueExpr);
987                                 method.Statements.Add (AddLinePragma (expr, builder));
988                         }
989                 }
990
991                 protected void CreateAssignStatementsFromAttributes (ControlBuilder builder)
992                 {
993                         this.dataBoundAtts = 0;
994                         IDictionary atts = builder.attribs;
995                         if (atts == null || atts.Count == 0)
996                                 return;
997                         
998                         foreach (string id in atts.Keys) {
999                                 if (InvariantCompareNoCase (id, "runat"))
1000                                         continue;
1001                                 // ID is assigned in BuildControltree
1002                                 if (InvariantCompareNoCase (id, "id"))
1003                                         continue;
1004                                 
1005 #if NET_2_0
1006                                 /* we skip SkinID here as it's assigned in BuildControlTree */
1007                                 if (InvariantCompareNoCase (id, "skinid"))
1008                                         continue;
1009                                 if (InvariantCompareNoCase (id, "meta:resourcekey"))
1010                                         continue; // ignore, this one's processed at the very end of
1011                                                   // the method
1012 #endif
1013                                 CreateAssignStatementFromAttribute (builder, id);
1014                         }
1015                 }
1016
1017                 void CreateDBAttributeMethod (ControlBuilder builder, string attr, CodeExpression code)
1018                 {
1019                         if (code == null)
1020                                 return;
1021
1022                         string id = builder.GetNextID (null);
1023                         string dbMethodName = "__DataBind_" + id;
1024                         CodeMemberMethod method = builder.method;
1025                         AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
1026
1027                         method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType);
1028                         CodeCastExpression cast;
1029                         CodeMethodReferenceExpression methodExpr;
1030                         CodeMethodInvokeExpression expr;
1031
1032                         CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
1033                         cast = new CodeCastExpression (typeof (IAttributeAccessor), targetExpr);
1034                         methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
1035                         expr = new CodeMethodInvokeExpression (methodExpr);
1036                         expr.Parameters.Add (new CodePrimitiveExpression (attr));
1037                         CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
1038                         tostring.Method = new CodeMethodReferenceExpression (
1039                                                         new CodeTypeReferenceExpression (typeof (Convert)),
1040                                                         "ToString");
1041                         tostring.Parameters.Add (code);
1042                         expr.Parameters.Add (tostring);
1043                         method.Statements.Add (expr);
1044                         mainClass.Members.Add (method);
1045                 }
1046
1047                 void AddRenderControl (ControlBuilder builder)
1048                 {
1049                         CodeIndexerExpression indexer = new CodeIndexerExpression ();
1050                         indexer.TargetObject = new CodePropertyReferenceExpression (
1051                                                         new CodeArgumentReferenceExpression ("parameterContainer"),
1052                                                         "Controls");
1053                                                         
1054                         indexer.Indices.Add (new CodePrimitiveExpression (builder.renderIndex));
1055                         
1056                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (indexer, "RenderControl");
1057                         invoke.Parameters.Add (new CodeArgumentReferenceExpression ("__output"));
1058                         builder.renderMethod.Statements.Add (invoke);
1059                         builder.renderIndex++;
1060                 }
1061
1062                 protected void AddChildCall (ControlBuilder parent, ControlBuilder child)
1063                 {
1064                         if (parent == null || child == null)
1065                                 return;
1066                         
1067                         CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.method.Name);
1068                         CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (m);
1069
1070                         object [] atts = null;
1071
1072                         if (child.ControlType != null)
1073                                 atts = child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true);
1074                         
1075                         if (atts != null && atts.Length > 0) {
1076                                 PartialCachingAttribute pca = (PartialCachingAttribute) atts [0];
1077                                 CodeTypeReferenceExpression cc = new CodeTypeReferenceExpression("System.Web.UI.StaticPartialCachingControl");
1078                                 CodeMethodInvokeExpression build = new CodeMethodInvokeExpression (cc, "BuildCachedControl");
1079                                 build.Parameters.Add (new CodeArgumentReferenceExpression("__ctrl"));
1080                                 build.Parameters.Add (new CodePrimitiveExpression (child.ID));
1081 #if NET_1_1
1082                                 if (pca.Shared)
1083                                         build.Parameters.Add (new CodePrimitiveExpression (child.ControlType.GetHashCode ().ToString ()));
1084                                 else
1085 #endif
1086                                         build.Parameters.Add (new CodePrimitiveExpression (Guid.NewGuid ().ToString ()));
1087                                         
1088                                 build.Parameters.Add (new CodePrimitiveExpression (pca.Duration));
1089                                 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByParams));
1090                                 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByControls));
1091                                 build.Parameters.Add (new CodePrimitiveExpression (pca.VaryByCustom));
1092                                 build.Parameters.Add (new CodeDelegateCreateExpression (
1093                                                               new CodeTypeReference (typeof (System.Web.UI.BuildMethod)),
1094                                                               thisRef, child.method.Name));
1095
1096                                 parent.methodStatements.Add (AddLinePragma (build, parent));
1097                                 if (parent.HasAspCode)
1098                                         AddRenderControl (parent);
1099                                 return;
1100                         }
1101                                 
1102                         if (child.isProperty || parent.ChildrenAsProperties) {
1103                                 expr.Parameters.Add (new CodeFieldReferenceExpression (ctrlVar, child.TagName));
1104                                 parent.methodStatements.Add (AddLinePragma (expr, parent));
1105                                 return;
1106                         }
1107
1108                         parent.methodStatements.Add (AddLinePragma (expr, parent));
1109                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, child.ID);
1110                         if (parent.ControlType == null || typeof (IParserAccessor).IsAssignableFrom (parent.ControlType))
1111                                 AddParsedSubObjectStmt (parent, field);
1112                         else {
1113                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (ctrlVar, "Add");
1114                                 invoke.Parameters.Add (field);
1115                                 parent.methodStatements.Add (AddLinePragma (invoke, parent));
1116                         }
1117                                 
1118                         if (parent.HasAspCode)
1119                                 AddRenderControl (parent);
1120                 }
1121
1122                 void AddTemplateInvocation (ControlBuilder builder, string name, string methodName)
1123                 {
1124                         CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
1125
1126                         CodeDelegateCreateExpression newBuild = new CodeDelegateCreateExpression (
1127                                 new CodeTypeReference (typeof (BuildTemplateMethod)), thisRef, methodName);
1128
1129                         CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
1130                         newCompiled.Parameters.Add (newBuild);
1131
1132                         CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
1133                         builder.method.Statements.Add (AddLinePragma (assign, builder));
1134                 }
1135
1136 #if NET_2_0
1137                 void AddBindableTemplateInvocation (ControlBuilder builder, string name, string methodName, string extractMethodName)
1138                 {
1139                         CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
1140
1141                         CodeDelegateCreateExpression newBuild = new CodeDelegateCreateExpression (
1142                                 new CodeTypeReference (typeof (BuildTemplateMethod)), thisRef, methodName);
1143
1144                         CodeDelegateCreateExpression newExtract = new CodeDelegateCreateExpression (
1145                                 new CodeTypeReference (typeof (ExtractTemplateValuesMethod)), thisRef, extractMethodName);
1146
1147                         CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledBindableTemplateBuilder));
1148                         newCompiled.Parameters.Add (newBuild);
1149                         newCompiled.Parameters.Add (newExtract);
1150                         
1151                         CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
1152                         builder.method.Statements.Add (AddLinePragma (assign, builder));
1153                 }
1154                 
1155                 string CreateExtractValuesMethod (TemplateBuilder builder)
1156                 {
1157                         CodeMemberMethod method = new CodeMemberMethod ();
1158                         method.Name = "__ExtractValues_" + builder.ID;
1159                         method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
1160                         method.ReturnType = new CodeTypeReference (typeof(IOrderedDictionary));
1161                         
1162                         CodeParameterDeclarationExpression arg = new CodeParameterDeclarationExpression ();
1163                         arg.Type = new CodeTypeReference (typeof (Control));
1164                         arg.Name = "__container";
1165                         method.Parameters.Add (arg);
1166                         mainClass.Members.Add (method);
1167                         
1168                         CodeObjectCreateExpression newTable = new CodeObjectCreateExpression ();
1169                         newTable.CreateType = new CodeTypeReference (typeof(OrderedDictionary));
1170                         method.Statements.Add (new CodeVariableDeclarationStatement (typeof(OrderedDictionary), "__table", newTable));
1171                         CodeVariableReferenceExpression tableExp = new CodeVariableReferenceExpression ("__table");
1172                         
1173                         if (builder.Bindings != null) {
1174                                 Hashtable hash = new Hashtable ();
1175                                 foreach (TemplateBinding binding in builder.Bindings) {
1176                                         CodeConditionStatement sif;
1177                                         CodeVariableReferenceExpression control;
1178                                         CodeAssignStatement assign;
1179
1180                                         if (hash [binding.ControlId] == null) {
1181
1182                                                 CodeVariableDeclarationStatement dec = new CodeVariableDeclarationStatement (binding.ControlType, binding.ControlId);
1183                                                 method.Statements.Add (dec);
1184                                                 CodeVariableReferenceExpression cter = new CodeVariableReferenceExpression ("__container");
1185                                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (cter, "FindControl");
1186                                                 invoke.Parameters.Add (new CodePrimitiveExpression (binding.ControlId));
1187
1188                                                 assign = new CodeAssignStatement ();
1189                                                 control = new CodeVariableReferenceExpression (binding.ControlId);
1190                                                 assign.Left = control;
1191                                                 assign.Right = new CodeCastExpression (binding.ControlType, invoke);
1192                                                 method.Statements.Add (assign);
1193
1194                                                 sif = new CodeConditionStatement ();
1195                                                 sif.Condition = new CodeBinaryOperatorExpression (control, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
1196
1197                                                 method.Statements.Add (sif);
1198
1199                                                 hash [binding.ControlId] = sif;
1200                                         }
1201
1202                                         sif = (CodeConditionStatement) hash [binding.ControlId];
1203                                         control = new CodeVariableReferenceExpression (binding.ControlId);
1204                                         assign = new CodeAssignStatement ();
1205                                         assign.Left = new CodeIndexerExpression (tableExp, new CodePrimitiveExpression (binding.FieldName));
1206                                         assign.Right = new CodePropertyReferenceExpression (control, binding.ControlProperty);
1207                                         sif.TrueStatements.Add (assign);
1208                                 }
1209                         }
1210
1211                         method.Statements.Add (new CodeMethodReturnStatement (tableExp));
1212                         return method.Name;
1213                 }
1214
1215                 void AddContentTemplateInvocation (ContentBuilderInternal cbuilder, CodeMemberMethod method, string methodName)
1216                 {
1217                         CodeDelegateCreateExpression newBuild = new CodeDelegateCreateExpression (
1218                                 new CodeTypeReference (typeof (BuildTemplateMethod)), thisRef, methodName);
1219
1220                         CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
1221                         newCompiled.Parameters.Add (newBuild);
1222                         
1223                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (thisRef, "AddContentTemplate");
1224                         invoke.Parameters.Add (new CodePrimitiveExpression (cbuilder.ContentPlaceHolderID));
1225                         invoke.Parameters.Add (newCompiled);
1226
1227                         method.Statements.Add (AddLinePragma (invoke, cbuilder));
1228                 }
1229 #endif
1230
1231                 void AddCodeRender (ControlBuilder parent, CodeRenderBuilder cr)
1232                 {
1233                         if (cr.Code == null || cr.Code.Trim () == "")
1234                                 return;
1235
1236                         if (!cr.IsAssign) {
1237                                 CodeSnippetStatement code = new CodeSnippetStatement (cr.Code);
1238                                 parent.renderMethod.Statements.Add (AddLinePragma (code, cr));
1239                                 return;
1240                         }
1241
1242                         CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression ();
1243                         expr.Method = new CodeMethodReferenceExpression (
1244                                                         new CodeArgumentReferenceExpression ("__output"),
1245                                                         "Write");
1246
1247                         expr.Parameters.Add (new CodeSnippetExpression (cr.Code));
1248                         parent.renderMethod.Statements.Add (AddLinePragma (expr, cr));
1249                 }
1250
1251                 static PropertyInfo GetContainerProperty (Type type, string[] propNames)
1252                 {
1253                         PropertyInfo prop;
1254                         
1255                         foreach (string name in propNames) {
1256                                 prop = type.GetProperty (name, noCaseFlags & ~BindingFlags.NonPublic);
1257                                 if (prop != null)
1258                                         return prop;
1259                         }
1260
1261                         return null;
1262                 }
1263
1264 #if !NET_2_0
1265                 static string[] containerPropNames = {"Items", "Rows"};
1266 #endif
1267                 
1268                 static Type GetContainerType (ControlBuilder builder)
1269                 {
1270                         Type type = builder.BindingContainerType;
1271                         
1272 #if NET_2_0
1273                         return type;
1274 #else
1275                         
1276                         PropertyInfo prop = GetContainerProperty (type, containerPropNames);
1277                         if (prop == null)
1278                                 return type;
1279
1280                         Type ptype = prop.PropertyType;
1281                         if (!typeof (ICollection).IsAssignableFrom (ptype))
1282                                 return type;
1283
1284                         prop = ptype.GetProperty ("Item", noCaseFlags & ~BindingFlags.NonPublic);
1285                         if (prop == null)
1286                                 return type;
1287
1288                         return prop.PropertyType;
1289 #endif
1290                 }
1291                 
1292                 CodeMemberMethod CreateDBMethod (ControlBuilder builder, string name, Type container, Type target)
1293                 {
1294                         CodeMemberMethod method = new CodeMemberMethod ();
1295                         method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
1296                         method.Name = name;
1297                         method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "sender"));
1298                         method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (EventArgs), "e"));
1299
1300                         CodeTypeReference containerRef = new CodeTypeReference (container);
1301                         CodeTypeReference targetRef = new CodeTypeReference (target);
1302
1303                         CodeVariableDeclarationStatement decl = new CodeVariableDeclarationStatement();
1304                         decl.Name = "Container";
1305                         decl.Type = containerRef;
1306                         method.Statements.Add (decl);
1307                         
1308                         decl = new CodeVariableDeclarationStatement();
1309                         decl.Name = "target";
1310                         decl.Type = targetRef;
1311                         method.Statements.Add (decl);
1312
1313                         CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
1314                         CodeAssignStatement assign = new CodeAssignStatement ();
1315                         assign.Left = targetExpr;
1316                         assign.Right = new CodeCastExpression (targetRef, new CodeArgumentReferenceExpression ("sender"));
1317                         method.Statements.Add (AddLinePragma (assign, builder));
1318
1319                         assign = new CodeAssignStatement ();
1320                         assign.Left = new CodeVariableReferenceExpression ("Container");
1321                         assign.Right = new CodeCastExpression (containerRef,
1322                                                 new CodePropertyReferenceExpression (targetExpr, "BindingContainer"));
1323                         method.Statements.Add (AddLinePragma (assign, builder));
1324
1325                         return method;
1326                 }
1327
1328                 void AddDataBindingLiteral (ControlBuilder builder, DataBindingBuilder db)
1329                 {
1330                         if (db.Code == null || db.Code.Trim () == "")
1331                                 return;
1332
1333                         EnsureID (db);
1334                         CreateField (db, false);
1335
1336                         string dbMethodName = "__DataBind_" + db.ID;
1337                         // Add the method that builds the DataBoundLiteralControl
1338                         InitMethod (db, false, false);
1339                         CodeMemberMethod method = db.method;
1340                         AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
1341                         method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
1342
1343                         // Add the DataBind handler
1344                         method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), typeof (DataBoundLiteralControl));
1345
1346                         CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
1347                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
1348                         invoke.Method = new CodeMethodReferenceExpression (targetExpr, "SetDataBoundString");
1349                         invoke.Parameters.Add (new CodePrimitiveExpression (0));
1350
1351                         CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
1352                         tostring.Method = new CodeMethodReferenceExpression (
1353                                                         new CodeTypeReferenceExpression (typeof (Convert)),
1354                                                         "ToString");
1355                         tostring.Parameters.Add (new CodeSnippetExpression (db.Code));
1356                         invoke.Parameters.Add (tostring);
1357                         method.Statements.Add (AddLinePragma (invoke, builder));
1358                         
1359                         mainClass.Members.Add (method);
1360
1361                         AddChildCall (builder, db);
1362                 }
1363
1364                 void FlushText (ControlBuilder builder, StringBuilder sb)
1365                 {
1366                         if (sb.Length > 0) {
1367                                 AddLiteralSubObject (builder, sb.ToString ());
1368                                 sb.Length = 0;
1369                         }
1370                 }
1371
1372                 protected void CreateControlTree (ControlBuilder builder, bool inTemplate, bool childrenAsProperties)
1373                 {
1374                         EnsureID (builder);
1375                         bool isTemplate = (typeof (TemplateBuilder).IsAssignableFrom (builder.GetType ()));
1376                         
1377                         if (!isTemplate && !inTemplate) {
1378                                 CreateField (builder, true);
1379                         } else if (!isTemplate) {
1380                                 bool doCheck = false;
1381                                 
1382 #if NET_2_0
1383                                 bool singleInstance = false;
1384                                 ControlBuilder pb = builder.parentBuilder;
1385                                 TemplateBuilder tpb;
1386                                 while (pb != null) {
1387                                         tpb = pb as TemplateBuilder;
1388                                         if (tpb == null) {
1389                                                 pb = pb.parentBuilder;
1390                                                 continue;
1391                                         }
1392                                         
1393                                         if (tpb.TemplateInstance == TemplateInstance.Single)
1394                                                 singleInstance = true;
1395                                         break;
1396                                 }
1397                                 
1398                                 if (!singleInstance)
1399 #endif
1400                                         builder.ID = builder.GetNextID (null);
1401 #if NET_2_0
1402                                 else
1403                                         doCheck = true;
1404 #endif
1405                                 CreateField (builder, doCheck);
1406                         }
1407
1408                         InitMethod (builder, isTemplate, childrenAsProperties);
1409                         if (!isTemplate || builder.GetType () == typeof (RootBuilder))
1410                                 CreateAssignStatementsFromAttributes (builder);
1411
1412                         if (builder.Children != null && builder.Children.Count > 0) {
1413                                 StringBuilder sb = new StringBuilder ();
1414                                 foreach (object b in builder.Children) {
1415                                         if (b is string) {
1416                                                 sb.Append ((string) b);
1417                                                 continue;
1418                                         }
1419
1420                                         FlushText (builder, sb);
1421                                         if (b is ObjectTagBuilder) {
1422                                                 ProcessObjectTag ((ObjectTagBuilder) b);
1423                                                 continue;
1424                                         }
1425
1426                                         StringPropertyBuilder pb = b as StringPropertyBuilder;
1427                                         if (pb != null){
1428                                                 if (pb.Children != null && pb.Children.Count > 0) {
1429                                                         StringBuilder asb = new StringBuilder ();
1430                                                         foreach (string s in pb.Children)
1431                                                                 asb.Append (s);
1432                                                         CodeMemberMethod method = builder.method;
1433                                                         CodeAssignStatement assign = new CodeAssignStatement ();
1434                                                         assign.Left = new CodePropertyReferenceExpression (ctrlVar, pb.PropertyName);
1435                                                         assign.Right = new CodePrimitiveExpression (asb.ToString ());
1436                                                         method.Statements.Add (AddLinePragma (assign, builder));
1437                                                 }
1438                                                 continue;
1439                                         }
1440
1441 #if NET_2_0
1442                                         if (b is ContentBuilderInternal) {
1443                                                 ContentBuilderInternal cb = (ContentBuilderInternal) b;
1444                                                 CreateControlTree (cb, false, true);
1445                                                 AddContentTemplateInvocation (cb, builder.method, cb.method.Name);
1446                                                 continue;
1447                                         }
1448 #endif
1449                                         // Ignore TemplateBuilders - they are processed in InitMethod
1450                                         if (b is TemplateBuilder)
1451                                                 continue;
1452
1453                                         if (b is CodeRenderBuilder) {
1454                                                 AddCodeRender (builder, (CodeRenderBuilder) b);
1455                                                 continue;
1456                                         }
1457
1458                                         if (b is DataBindingBuilder) {
1459                                                 AddDataBindingLiteral (builder, (DataBindingBuilder) b);
1460                                                 continue;
1461                                         }
1462                                         
1463                                         if (b is ControlBuilder) {
1464                                                 ControlBuilder child = (ControlBuilder) b;
1465                                                 CreateControlTree (child, inTemplate, builder.ChildrenAsProperties);
1466                                                 AddChildCall (builder, child);
1467                                                 continue;
1468                                         }
1469
1470                                         throw new Exception ("???");
1471                                 }
1472
1473                                 FlushText (builder, sb);
1474                         }
1475
1476                         if (builder.defaultPropertyBuilder != null) {
1477                                 ControlBuilder b = builder.defaultPropertyBuilder;
1478                                 CreateControlTree (b, false, true);
1479                                 AddChildCall (builder, b);
1480                         }
1481
1482                         if (builder.HasAspCode) {
1483                                 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1484                                 m.TargetObject = thisRef;
1485                                 m.MethodName = builder.renderMethod.Name;
1486
1487                                 CodeDelegateCreateExpression create = new CodeDelegateCreateExpression ();
1488                                 create.DelegateType = new CodeTypeReference (typeof (RenderMethod));
1489                                 create.TargetObject = thisRef;
1490                                 create.MethodName = builder.renderMethod.Name;
1491
1492                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
1493                                 invoke.Method = new CodeMethodReferenceExpression (ctrlVar, "SetRenderMethodDelegate");
1494                                 invoke.Parameters.Add (create);
1495
1496                                 builder.methodStatements.Add (invoke);
1497                         }
1498
1499 #if NET_2_0
1500                         if (builder is RootBuilder)
1501                                 if (!String.IsNullOrEmpty (parser.MetaResourceKey))
1502                                         AssignPropertiesFromResources (builder, parser.BaseType, parser.MetaResourceKey);
1503                         
1504                         if ((!isTemplate || builder is RootBuilder) && builder.attribs != null && builder.attribs ["meta:resourcekey"] != null)
1505                                 CreateAssignStatementFromAttribute (builder, "meta:resourcekey");
1506 #endif
1507
1508                         if (!childrenAsProperties && typeof (Control).IsAssignableFrom (builder.ControlType))
1509                                 builder.method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
1510                 }
1511                 
1512                 protected internal override void CreateMethods ()
1513                 {
1514                         base.CreateMethods ();
1515
1516                         CreateProperties ();
1517                         CreateControlTree (parser.RootBuilder, false, false);
1518                         CreateFrameworkInitializeMethod ();
1519                 }
1520
1521 #if NET_2_0
1522                 void CallBaseFrameworkInitialize (CodeMemberMethod method)
1523                 {
1524                         CodeBaseReferenceExpression baseRef = new CodeBaseReferenceExpression ();
1525                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (baseRef, "FrameworkInitialize");
1526                         method.Statements.Add (invoke);
1527                 }
1528 #endif
1529                 
1530                 void CallSetStringResourcePointer (CodeMemberMethod method)
1531                 {
1532                         CodeFieldReferenceExpression stringResource = GetMainClassFieldReferenceExpression ("__stringResource");
1533                         method.Statements.Add (
1534                                 new CodeMethodInvokeExpression (
1535                                         thisRef,
1536                                         "SetStringResourcePointer",
1537                                         new CodeExpression[] {stringResource, new CodePrimitiveExpression (0)})
1538                         );
1539                 }
1540                 
1541                 void CreateFrameworkInitializeMethod ()
1542                 {
1543                         CodeMemberMethod method = new CodeMemberMethod ();
1544                         method.Name = "FrameworkInitialize";
1545                         method.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1546                         PrependStatementsToFrameworkInitialize (method);
1547 #if NET_2_0
1548                         CallBaseFrameworkInitialize (method);
1549 #endif
1550                         CallSetStringResourcePointer (method);
1551                         AppendStatementsToFrameworkInitialize (method);
1552                         mainClass.Members.Add (method);
1553                 }
1554
1555                 protected virtual void PrependStatementsToFrameworkInitialize (CodeMemberMethod method)
1556                 {
1557                 }
1558
1559                 protected virtual void AppendStatementsToFrameworkInitialize (CodeMemberMethod method)
1560                 {
1561                         if (!parser.EnableViewState) {
1562                                 CodeAssignStatement stmt = new CodeAssignStatement ();
1563                                 stmt.Left = new CodePropertyReferenceExpression (thisRef, "EnableViewState");
1564                                 stmt.Right = new CodePrimitiveExpression (false);
1565                                 method.Statements.Add (stmt);
1566                         }
1567
1568                         CodeMethodReferenceExpression methodExpr;
1569                         methodExpr = new CodeMethodReferenceExpression (thisRef, "__BuildControlTree");
1570                         CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (methodExpr, thisRef);
1571                         method.Statements.Add (new CodeExpressionStatement (expr));
1572                 }
1573
1574                 protected override void AddApplicationAndSessionObjects ()
1575                 {
1576                         foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.ApplicationObjects) {
1577                                 CreateFieldForObject (tag.Type, tag.ObjectID);
1578                                 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, true, false);
1579                         }
1580
1581                         foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.SessionObjects) {
1582                                 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, false, false);
1583                         }
1584                 }
1585
1586                 protected override void CreateStaticFields ()
1587                 {
1588                         base.CreateStaticFields ();
1589
1590                         CodeMemberField fld = new CodeMemberField (typeof (object), "__stringResource");
1591                         fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
1592                         fld.InitExpression = new CodePrimitiveExpression (null);
1593                         mainClass.Members.Add (fld);
1594                 }
1595                 
1596                 protected void ProcessObjectTag (ObjectTagBuilder tag)
1597                 {
1598                         string fieldName = CreateFieldForObject (tag.Type, tag.ObjectID);
1599                         CreatePropertyForObject (tag.Type, tag.ObjectID, fieldName, false);
1600                 }
1601
1602                 void CreateProperties ()
1603                 {
1604                         if (!parser.AutoEventWireup) {
1605                                 CreateAutoEventWireup ();
1606                         } else {
1607                                 CreateAutoHandlers ();
1608                         }
1609
1610                         CreateApplicationInstance ();
1611                 }
1612                 
1613                 void CreateApplicationInstance ()
1614                 {
1615                         CodeMemberProperty prop = new CodeMemberProperty ();
1616                         Type appType = typeof (HttpApplication);
1617                         prop.Type = new CodeTypeReference (appType);
1618                         prop.Name = "ApplicationInstance";
1619                         prop.Attributes = MemberAttributes.Family | MemberAttributes.Final;
1620
1621                         CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression (thisRef, "Context");
1622
1623                         propRef = new CodePropertyReferenceExpression (propRef, "ApplicationInstance");
1624
1625                         CodeCastExpression cast = new CodeCastExpression (appType.FullName, propRef);
1626                         prop.GetStatements.Add (new CodeMethodReturnStatement (cast));
1627 #if NET_2_0
1628                         if (partialClass != null)
1629                                 partialClass.Members.Add (prop);
1630                         else
1631 #endif
1632                         mainClass.Members.Add (prop);
1633                 }
1634
1635                 void CreateAutoHandlers ()
1636                 {
1637                         // Create AutoHandlers property
1638                         CodeMemberProperty prop = new CodeMemberProperty ();
1639                         prop.Type = new CodeTypeReference (typeof (int));
1640                         prop.Name = "AutoHandlers";
1641                         prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1642                         
1643                         CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
1644                         CodeFieldReferenceExpression fldRef ;
1645                         fldRef = new CodeFieldReferenceExpression (mainClassExpr, "__autoHandlers");
1646                         ret.Expression = fldRef;
1647                         prop.GetStatements.Add (ret);
1648                         prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ()));
1649
1650 #if NET_2_0
1651                         CodeAttributeDeclaration attr = new CodeAttributeDeclaration ("System.Obsolete");
1652                         prop.CustomAttributes.Add (attr);
1653 #endif
1654                         
1655                         mainClass.Members.Add (prop);
1656
1657                         // Add the __autoHandlers field
1658                         CodeMemberField fld = new CodeMemberField (typeof (int), "__autoHandlers");
1659                         fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
1660                         mainClass.Members.Add (fld);
1661                 }
1662
1663                 void CreateAutoEventWireup ()
1664                 {
1665                         // The getter returns false
1666                         CodeMemberProperty prop = new CodeMemberProperty ();
1667                         prop.Type = new CodeTypeReference (typeof (bool));
1668                         prop.Name = "SupportAutoEvents";
1669                         prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1670                         prop.GetStatements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (false)));
1671                         mainClass.Members.Add (prop);
1672                 }
1673
1674 #if NET_2_0
1675                 protected virtual string HandleUrlProperty (string str, MemberInfo member)
1676                 {
1677                         return str;
1678                 }
1679 #endif
1680
1681                 TypeConverter GetConverterForMember (MemberInfo member)
1682                 {
1683                         TypeConverterAttribute tca = null;
1684                         object[] attrs;
1685                         
1686 #if NET_2_0
1687                         attrs = member.GetCustomAttributes (typeof (TypeConverterAttribute), true);
1688                         if (attrs.Length > 0)
1689                                 tca = attrs [0] as TypeConverterAttribute;
1690 #else
1691                         attrs = member.GetCustomAttributes (true);
1692                         
1693                         foreach (object attr in attrs) {
1694                                 tca = attr as TypeConverterAttribute;
1695                                 
1696                                 if (tca != null)
1697                                         break;
1698                         }
1699 #endif
1700
1701                         if (tca == null)
1702                                 return null;
1703
1704                         string typeName = tca.ConverterTypeName;
1705                         if (typeName == null || typeName.Length == 0)
1706                                 return null;
1707
1708                         Type t = null;
1709                         try {
1710                                 t = HttpApplication.LoadType (typeName);
1711                         } catch (Exception) {
1712                                 // ignore
1713                         }
1714
1715                         if (t == null)
1716                                 return null;
1717
1718                         return (TypeConverter) Activator.CreateInstance (t);
1719                 }
1720                 
1721                 CodeExpression CreateNullableExpression (Type type, CodeExpression inst, bool nullable)
1722                 {
1723 #if NET_2_0
1724                         if (!nullable)
1725                                 return inst;
1726                         
1727                         return new CodeObjectCreateExpression (
1728                                 type,
1729                                 new CodeExpression[] {inst}
1730                         );
1731 #else
1732                         return inst;
1733 #endif
1734                 }
1735
1736                 bool SafeCanConvertFrom (Type type, TypeConverter cvt)
1737                 {
1738                         try {
1739                                 return cvt.CanConvertFrom (type);
1740                         } catch (NotImplementedException) {
1741                                 return false;
1742                         }
1743                 }
1744
1745                 bool SafeCanConvertTo (Type type, TypeConverter cvt)
1746                 {
1747                         try {
1748                                 return cvt.CanConvertTo (type);
1749                         } catch (NotImplementedException) {
1750                                 return false;
1751                         }
1752                 }
1753                 
1754                 CodeExpression GetExpressionFromString (Type type, string str, MemberInfo member)
1755                 {
1756                         TypeConverter cvt = GetConverterForMember (member);
1757                         if (cvt != null && !SafeCanConvertFrom (typeof (string), cvt))
1758                                 cvt = null;
1759                         
1760                         object convertedFromAttr = null;
1761                         bool preConverted = false;
1762                         if (cvt != null && str != null) {
1763                                 convertedFromAttr = cvt.ConvertFromInvariantString (str);
1764                                 if (convertedFromAttr != null) {
1765                                         type = convertedFromAttr.GetType ();
1766                                         preConverted = true;
1767                                 }
1768                         }
1769
1770                         bool wasNullable = false;
1771                         Type originalType = type;
1772 #if NET_2_0
1773                         if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
1774                                 Type[] types = type.GetGenericArguments();
1775                                 originalType = type;
1776                                 type = types[0]; // we're interested only in the first type here
1777                                 wasNullable = true;
1778                         }
1779 #endif
1780                         if (type == typeof (string)) {
1781                                 if (preConverted)
1782                                         return CreateNullableExpression (originalType,
1783                                                                          new CodePrimitiveExpression ((string) convertedFromAttr),
1784                                                                          wasNullable);
1785                                 
1786 #if NET_2_0
1787                                 object[] urlAttr = member.GetCustomAttributes (typeof (UrlPropertyAttribute), true);
1788                                 if (urlAttr.Length != 0)
1789                                         str = HandleUrlProperty (str, member);
1790 #endif
1791                                 return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
1792                         } else if (type == typeof (bool)) {
1793                                 if (preConverted)
1794                                         return CreateNullableExpression (originalType,
1795                                                                          new CodePrimitiveExpression ((bool) convertedFromAttr),
1796                                                                          wasNullable);
1797                                 
1798                                 if (str == null || str == "" || InvariantCompareNoCase (str, "true"))
1799                                         return CreateNullableExpression (originalType, new CodePrimitiveExpression (true), wasNullable);
1800                                 else if (InvariantCompareNoCase (str, "false"))
1801                                         return CreateNullableExpression (originalType, new CodePrimitiveExpression (false), wasNullable);
1802 #if NET_2_0
1803                                 else if (wasNullable && InvariantCompareNoCase(str, "null"))
1804                                         return new CodePrimitiveExpression (null);
1805 #endif
1806                                 else
1807                                         throw new ParseException (currentLocation,
1808                                                         "Value '" + str  + "' is not a valid boolean.");
1809                         } else if (type == monoTypeType)
1810                                 type = typeof (System.Type);
1811                         
1812                         if (str == null)
1813                                 return new CodePrimitiveExpression (null);
1814
1815                         if (type.IsPrimitive)
1816                                 return CreateNullableExpression (originalType,
1817                                                                  new CodePrimitiveExpression (
1818                                                                          Convert.ChangeType (preConverted ? convertedFromAttr : str,
1819                                                                                              type, CultureInfo.InvariantCulture)),
1820                                                                  wasNullable);
1821
1822                         if (type == typeof (string [])) {
1823                                 string [] subs;
1824
1825                                 if (preConverted)
1826                                         subs = (string[])convertedFromAttr;
1827                                 else
1828                                         subs = str.Split (',');
1829                                 CodeArrayCreateExpression expr = new CodeArrayCreateExpression ();
1830                                 expr.CreateType = new CodeTypeReference (typeof (string));
1831                                 foreach (string v in subs)
1832                                         expr.Initializers.Add (new CodePrimitiveExpression (v.Trim ()));
1833
1834                                 return CreateNullableExpression (originalType, expr, wasNullable);
1835                         }
1836                         
1837                         if (type == typeof (Color)) {
1838                                 Color c;
1839                                 
1840                                 if (!preConverted) {
1841                                         if (colorConverter == null)
1842                                                 colorConverter = TypeDescriptor.GetConverter (typeof (Color));
1843                                 
1844                                         if (str.Trim().Length == 0) {
1845                                                 CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (typeof (Color));
1846                                                 return CreateNullableExpression (originalType,
1847                                                                                  new CodeFieldReferenceExpression (ft, "Empty"),
1848                                                                                  wasNullable);
1849                                         }
1850
1851                                         try {
1852                                                 if (str.IndexOf (',') == -1) {
1853                                                         c = (Color) colorConverter.ConvertFromString (str);
1854                                                 } else {
1855                                                         int [] argb = new int [4];
1856                                                         argb [0] = 255;
1857
1858                                                         string [] parts = str.Split (',');
1859                                                         int length = parts.Length;
1860                                                         if (length < 3)
1861                                                                 throw new Exception ();
1862
1863                                                         int basei = (length == 4) ? 0 : 1;
1864                                                         for (int i = length - 1; i >= 0; i--) {
1865                                                                 argb [basei + i] = (int) Byte.Parse (parts [i]);
1866                                                         }
1867                                                         c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]);
1868                                                 }
1869                                         } catch (Exception e) {
1870                                                 // Hack: "LightGrey" is accepted, but only for ASP.NET, as the
1871                                                 // TypeConverter for Color fails to ConvertFromString.
1872                                                 // Hence this hack...
1873                                                 if (InvariantCompareNoCase ("LightGrey", str)) {
1874                                                         c = Color.LightGray;
1875                                                 } else {
1876                                                         throw new ParseException (currentLocation,
1877                                                                                   "Color " + str + " is not a valid color.", e);
1878                                                 }
1879                                         }
1880                                 } else
1881                                         c = (Color)convertedFromAttr;
1882                                 
1883                                 if (c.IsKnownColor) {
1884                                         CodeFieldReferenceExpression expr = new CodeFieldReferenceExpression ();
1885                                         if (c.IsSystemColor)
1886                                                 type = typeof (SystemColors);
1887
1888                                         expr.TargetObject = new CodeTypeReferenceExpression (type);
1889                                         expr.FieldName = c.Name;
1890                                         return CreateNullableExpression (originalType, expr, wasNullable);
1891                                 } else {
1892                                         CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1893                                         m.TargetObject = new CodeTypeReferenceExpression (type);
1894                                         m.MethodName = "FromArgb";
1895                                         CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
1896                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.A));
1897                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.R));
1898                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.G));
1899                                         invoke.Parameters.Add (new CodePrimitiveExpression (c.B));
1900                                         return CreateNullableExpression (originalType, invoke, wasNullable);
1901                                 }
1902                         }
1903
1904                         TypeConverter converter = preConverted ? cvt :
1905                                 wasNullable ? TypeDescriptor.GetConverter (type) :
1906                                 TypeDescriptor.GetProperties (member.DeclaringType) [member.Name].Converter;
1907
1908                         if (preConverted || (converter != null && SafeCanConvertFrom (typeof (string), converter))) {
1909                                 object value = preConverted ? convertedFromAttr : converter.ConvertFromInvariantString (str);
1910
1911                                 if (SafeCanConvertTo (typeof (InstanceDescriptor), converter)) {
1912                                         InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor));
1913                                         if (wasNullable)
1914                                                 return CreateNullableExpression (originalType, GenerateInstance (idesc, true),
1915                                                                                  wasNullable);
1916
1917                                         return new CodeCastExpression (type, GenerateInstance (idesc, true));
1918                                 }
1919
1920                                 CodeExpression exp = GenerateObjectInstance (value, false);
1921                                 if (exp != null)
1922                                         return CreateNullableExpression (originalType, exp, wasNullable);
1923                                 
1924                                 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1925                                 m.TargetObject = new CodeTypeReferenceExpression (typeof (TypeDescriptor));
1926                                 m.MethodName = "GetConverter";
1927                                 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
1928                                 CodeTypeReference tref = new CodeTypeReference (type);
1929                                 invoke.Parameters.Add (new CodeTypeOfExpression (tref));
1930                                 
1931                                 invoke = new CodeMethodInvokeExpression (invoke, "ConvertFrom");
1932                                 invoke.Parameters.Add (new CodePrimitiveExpression (str));
1933
1934                                 if (wasNullable)
1935                                         return CreateNullableExpression (originalType, invoke, wasNullable);
1936
1937                                 return new CodeCastExpression (type, invoke);
1938                         }
1939
1940                         Console.WriteLine ("Unknown type: " + type + " value: " + str);
1941                         
1942                         return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
1943                 }
1944                 
1945                 CodeExpression GenerateInstance (InstanceDescriptor idesc, bool throwOnError)
1946                 {
1947                         CodeExpression[] parameters = new CodeExpression [idesc.Arguments.Count];
1948                         int n = 0;
1949                         foreach (object ob in idesc.Arguments) {
1950                                 CodeExpression exp = GenerateObjectInstance (ob, throwOnError);
1951                                 if (exp == null) return null;
1952                                 parameters [n++] = exp;
1953                         }
1954                         
1955                         switch (idesc.MemberInfo.MemberType) {
1956                         case MemberTypes.Constructor:
1957                                 CodeTypeReference tob = new CodeTypeReference (idesc.MemberInfo.DeclaringType);
1958                                 return new CodeObjectCreateExpression (tob, parameters);
1959
1960                         case MemberTypes.Method:
1961                                 CodeTypeReferenceExpression mt = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
1962                                 return new CodeMethodInvokeExpression (mt, idesc.MemberInfo.Name, parameters);
1963
1964                         case MemberTypes.Field:
1965                                 CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
1966                                 return new CodeFieldReferenceExpression (ft, idesc.MemberInfo.Name);
1967
1968                         case MemberTypes.Property:
1969                                 CodeTypeReferenceExpression pt = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
1970                                 return new CodePropertyReferenceExpression (pt, idesc.MemberInfo.Name);
1971                         }
1972                         throw new ParseException (currentLocation, "Invalid instance type.");
1973                 }
1974                 
1975                 CodeExpression GenerateObjectInstance (object value, bool throwOnError)
1976                 {
1977                         if (value == null)
1978                                 return new CodePrimitiveExpression (null);
1979
1980                         if (value is System.Type) {
1981                                 CodeTypeReference tref = new CodeTypeReference (value.ToString ());
1982                                 return new CodeTypeOfExpression (tref);
1983                         }
1984                         
1985                         Type t = value.GetType ();
1986
1987                         if (t.IsPrimitive || value is string)
1988                                 return new CodePrimitiveExpression (value);
1989                         
1990                         if (t.IsArray) {
1991                                 Array ar = (Array) value;
1992                                 CodeExpression[] items = new CodeExpression [ar.Length];
1993                                 for (int n=0; n<ar.Length; n++) {
1994                                         CodeExpression exp = GenerateObjectInstance (ar.GetValue (n), throwOnError);
1995                                         if (exp == null) return null; 
1996                                         items [n] = exp;
1997                                 }
1998                                 return new CodeArrayCreateExpression (new CodeTypeReference (t), items);
1999                         }
2000                         
2001                         TypeConverter converter = TypeDescriptor.GetConverter (t);
2002                         if (converter != null && converter.CanConvertTo (typeof (InstanceDescriptor))) {
2003                                 InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor));
2004                                 return GenerateInstance (idesc, throwOnError);
2005                         }
2006                         
2007                         InstanceDescriptor desc = GetDefaultInstanceDescriptor (value);
2008                         if (desc != null) return GenerateInstance (desc, throwOnError);
2009                         
2010                         if (throwOnError)
2011                                 throw new ParseException (currentLocation, "Cannot generate an instance for the type: " + t);
2012                         else
2013                                 return null;
2014                 }
2015                 
2016                 InstanceDescriptor GetDefaultInstanceDescriptor (object value)
2017                 {
2018                         if (value is System.Web.UI.WebControls.Unit) {
2019                                 System.Web.UI.WebControls.Unit s = (System.Web.UI.WebControls.Unit) value;
2020                                 ConstructorInfo c = typeof(System.Web.UI.WebControls.Unit).GetConstructor (
2021                                         BindingFlags.Instance | BindingFlags.Public,
2022                                         null,
2023                                         new Type[] {typeof(double), typeof(System.Web.UI.WebControls.UnitType)},
2024                                         null);
2025                                 
2026                                 return new InstanceDescriptor (c, new object[] {s.Value, s.Type});
2027                         }
2028                         
2029                         if (value is System.Web.UI.WebControls.FontUnit) {
2030                                 System.Web.UI.WebControls.FontUnit s = (System.Web.UI.WebControls.FontUnit) value;
2031
2032                                 Type cParamType = null;
2033                                 object cParam = null;
2034
2035                                 switch (s.Type) {
2036                                         case FontSize.AsUnit:
2037                                         case FontSize.NotSet:
2038                                                 cParamType = typeof (System.Web.UI.WebControls.Unit);
2039                                                 cParam = s.Unit;
2040                                                 break;
2041
2042                                         default:
2043                                                 cParamType = typeof (string);
2044                                                 cParam = s.Type.ToString ();
2045                                                 break;
2046                                 }
2047                                 
2048                                 ConstructorInfo c = typeof(System.Web.UI.WebControls.FontUnit).GetConstructor (
2049                                         BindingFlags.Instance | BindingFlags.Public,
2050                                         null,
2051                                         new Type[] {cParamType},
2052                                         null);
2053                                 if (c != null)
2054                                         return new InstanceDescriptor (c, new object[] {cParam});
2055                         }
2056                         return null;
2057                 }
2058
2059 #if DEBUG
2060                 CodeMethodInvokeExpression CreateConsoleWriteLineCall (string format, params CodeExpression[] parms)
2061                 {
2062                         CodeMethodReferenceExpression cwl = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (System.Console)), "WriteLine");
2063                         CodeMethodInvokeExpression cwlCall = new CodeMethodInvokeExpression (cwl);
2064
2065                         cwlCall.Parameters.Add (new CodePrimitiveExpression (format));
2066                         if (parms != null && parms.Length > 0)
2067                                 foreach (CodeExpression expr in parms)
2068                                         cwlCall.Parameters.Add (expr);
2069
2070                         return cwlCall;
2071                 }
2072 #endif
2073         }
2074 }
2075
2076
2077