w32file-unix.c: In function 'mono_w32file_get_file_size':
[mono-project.git] / mcs / class / System.Web / System.Web.Compilation / TemplateControlCompiler.cs
blob7dab906cc2b71f96b603302c638dcd7ae55a9a2a
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)
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.
31 using System;
32 using System.CodeDom;
33 using System.Collections;
34 using System.ComponentModel;
35 using System.Configuration;
36 using System.Collections.Specialized;
37 using System.Collections.Generic;
38 using System.Drawing;
39 using System.Globalization;
40 using System.Reflection;
41 using System.Resources;
42 using System.Text;
43 using System.Web;
44 using System.Web.Caching;
45 using System.Web.Configuration;
46 using System.Web.UI;
47 using System.Web.UI.WebControls;
48 using System.Web.Util;
49 using System.ComponentModel.Design.Serialization;
50 using System.Text.RegularExpressions;
52 namespace System.Web.Compilation
54 class TemplateControlCompiler : BaseCompiler
56 static BindingFlags noCaseFlags = BindingFlags.Public | BindingFlags.NonPublic |
57 BindingFlags.Instance | BindingFlags.IgnoreCase;
58 static Type monoTypeType = Type.GetType ("System.MonoType");
60 TemplateControlParser parser;
61 int dataBoundAtts;
62 internal ILocation currentLocation;
64 static TypeConverter colorConverter;
66 internal static CodeVariableReferenceExpression ctrlVar = new CodeVariableReferenceExpression ("__ctrl");
68 List <string> masterPageContentPlaceHolders;
69 static Regex startsWithBindRegex = new Regex (@"^Bind\s*\(", RegexOptions.Compiled | RegexOptions.IgnoreCase);
70 // When modifying those, make sure to look at the SanitizeBindCall to make sure it
71 // picks up correct groups.
72 static Regex bindRegex = new Regex (@"Bind\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)\s*%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
73 static Regex bindRegexInValue = new Regex (@"Bind\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
74 static Regex evalRegexInValue = new Regex (@"(.*)Eval\s*\(\s*[""']+(.*?)[""']+((\s*,\s*[""']+(.*?)[""']+)?)\s*\)(.*)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
76 List <string> MasterPageContentPlaceHolders {
77 get {
78 if (masterPageContentPlaceHolders == null)
79 masterPageContentPlaceHolders = new List <string> ();
80 return masterPageContentPlaceHolders;
84 public TemplateControlCompiler (TemplateControlParser parser)
85 : base (parser)
87 this.parser = parser;
90 protected void EnsureID (ControlBuilder builder)
92 string id = builder.ID;
93 if (id == null || id.Trim () == String.Empty)
94 builder.ID = builder.GetNextID (null);
97 void CreateField (ControlBuilder builder, bool check)
99 if (builder == null || builder.ID == null || builder.ControlType == null)
100 return;
102 if (partialNameOverride [builder.ID] != null)
103 return;
105 MemberAttributes ma = MemberAttributes.Family;
106 currentLocation = builder.Location;
107 if (check && CheckBaseFieldOrProperty (builder.ID, builder.ControlType, ref ma))
108 return; // The field or property already exists in a base class and is accesible.
110 CodeMemberField field;
111 field = new CodeMemberField (builder.ControlType.FullName, builder.ID);
112 field.Attributes = ma;
113 field.Type.Options |= CodeTypeReferenceOptions.GlobalReference;
115 if (partialClass != null)
116 partialClass.Members.Add (AddLinePragma (field, builder));
117 else
118 mainClass.Members.Add (AddLinePragma (field, builder));
121 bool CheckBaseFieldOrProperty (string id, Type type, ref MemberAttributes ma)
123 FieldInfo fld = parser.BaseType.GetField (id, noCaseFlags);
125 Type other = null;
126 if (fld == null || fld.IsPrivate) {
127 PropertyInfo prop = parser.BaseType.GetProperty (id, noCaseFlags);
128 if (prop != null) {
129 MethodInfo setm = prop.GetSetMethod (true);
130 if (setm != null)
131 other = prop.PropertyType;
133 } else {
134 other = fld.FieldType;
137 if (other == null)
138 return false;
140 if (!other.IsAssignableFrom (type)) {
141 ma |= MemberAttributes.New;
142 return false;
145 return true;
148 void AddParsedSubObjectStmt (ControlBuilder builder, CodeExpression expr)
150 if (!builder.HaveParserVariable) {
151 CodeVariableDeclarationStatement p = new CodeVariableDeclarationStatement();
152 p.Name = "__parser";
153 p.Type = new CodeTypeReference (typeof (IParserAccessor));
154 p.InitExpression = new CodeCastExpression (typeof (IParserAccessor), ctrlVar);
155 builder.MethodStatements.Add (p);
156 builder.HaveParserVariable = true;
159 CodeVariableReferenceExpression var = new CodeVariableReferenceExpression ("__parser");
160 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (var, "AddParsedSubObject");
161 invoke.Parameters.Add (expr);
162 builder.MethodStatements.Add (AddLinePragma (invoke, builder));
165 CodeStatement CreateControlVariable (Type type, ControlBuilder builder, CodeMemberMethod method, CodeTypeReference ctrlTypeRef)
167 CodeObjectCreateExpression newExpr = new CodeObjectCreateExpression (ctrlTypeRef);
169 object [] atts = type != null ? type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true) : null;
170 if (atts != null && atts.Length > 0) {
171 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
172 if (att.NeedsTag)
173 newExpr.Parameters.Add (new CodePrimitiveExpression (builder.TagName));
174 } else if (builder is DataBindingBuilder) {
175 newExpr.Parameters.Add (new CodePrimitiveExpression (0));
176 newExpr.Parameters.Add (new CodePrimitiveExpression (1));
179 method.Statements.Add (new CodeVariableDeclarationStatement (ctrlTypeRef, "__ctrl"));
180 CodeAssignStatement assign = new CodeAssignStatement ();
181 assign.Left = ctrlVar;
182 assign.Right = newExpr;
184 return assign;
187 void InitMethod (ControlBuilder builder, bool isTemplate, bool childrenAsProperties)
189 currentLocation = builder.Location;
190 bool inBuildControlTree = builder is RootBuilder;
191 string tailname = (inBuildControlTree ? "Tree" : ("_" + builder.ID));
192 // bool isProperty = builder.IsProperty;
193 CodeMemberMethod method = new CodeMemberMethod ();
194 builder.Method = method;
195 builder.MethodStatements = method.Statements;
197 method.Name = "__BuildControl" + tailname;
198 method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
199 Type type = builder.ControlType;
201 /* in the case this is the __BuildControlTree
202 * method, allow subclasses to insert control
203 * specific code. */
204 if (inBuildControlTree) {
205 SetCustomAttributes (method);
206 AddStatementsToInitMethodTop (builder, method);
209 if (builder.HasAspCode) {
210 CodeMemberMethod renderMethod = new CodeMemberMethod ();
211 builder.RenderMethod = renderMethod;
212 renderMethod.Name = "__Render" + tailname;
213 renderMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
214 CodeParameterDeclarationExpression arg1 = new CodeParameterDeclarationExpression ();
215 arg1.Type = new CodeTypeReference (typeof (HtmlTextWriter));
216 arg1.Name = "__output";
217 CodeParameterDeclarationExpression arg2 = new CodeParameterDeclarationExpression ();
218 arg2.Type = new CodeTypeReference (typeof (Control));
219 arg2.Name = "parameterContainer";
220 renderMethod.Parameters.Add (arg1);
221 renderMethod.Parameters.Add (arg2);
222 mainClass.Members.Add (renderMethod);
225 if (childrenAsProperties || type == null) {
226 string typeString;
227 bool isGlobal = true;
228 bool returnsControl;
230 if (builder is RootBuilder) {
231 typeString = parser.ClassName;
232 isGlobal = false;
233 returnsControl = false;
234 } else {
235 returnsControl = builder.PropertyBuilderShouldReturnValue;
236 if (type != null && builder.IsProperty && !typeof (ITemplate).IsAssignableFrom (type)) {
237 typeString = type.FullName;
238 isGlobal = !type.IsPrimitive;
239 } else
240 typeString = "System.Web.UI.Control";
241 ProcessTemplateChildren (builder);
243 CodeTypeReference ctrlTypeRef = new CodeTypeReference (typeString);
244 if (isGlobal)
245 ctrlTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
247 if (returnsControl) {
248 method.ReturnType = ctrlTypeRef;
250 // $controlType _ctrl = new $controlType ($parameters);
252 method.Statements.Add (CreateControlVariable (type, builder, method, ctrlTypeRef));
253 } else
254 method.Parameters.Add (new CodeParameterDeclarationExpression (typeString, "__ctrl"));
255 } else {
256 CodeTypeReference ctrlTypeRef = new CodeTypeReference (type.FullName);
257 if (!type.IsPrimitive)
258 ctrlTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
260 if (typeof (Control).IsAssignableFrom (type))
261 method.ReturnType = ctrlTypeRef;
263 // $controlType _ctrl = new $controlType ($parameters);
265 method.Statements.Add (AddLinePragma (CreateControlVariable (type, builder, method, ctrlTypeRef), builder));
267 // this.$builderID = _ctrl;
269 CodeFieldReferenceExpression builderID = new CodeFieldReferenceExpression ();
270 builderID.TargetObject = thisRef;
271 builderID.FieldName = builder.ID;
272 CodeAssignStatement assign = new CodeAssignStatement ();
273 assign.Left = builderID;
274 assign.Right = ctrlVar;
275 method.Statements.Add (AddLinePragma (assign, builder));
277 if (typeof (UserControl).IsAssignableFrom (type)) {
278 CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression ();
279 mref.TargetObject = builderID;
280 mref.MethodName = "InitializeAsUserControl";
281 CodeMethodInvokeExpression initAsControl = new CodeMethodInvokeExpression (mref);
282 initAsControl.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page"));
283 method.Statements.Add (initAsControl);
286 if (builder.ParentTemplateBuilder is System.Web.UI.WebControls.ContentBuilderInternal) {
287 PropertyInfo pi;
289 try {
290 pi = type.GetProperty ("TemplateControl");
291 } catch (Exception) {
292 pi = null;
295 if (pi != null && pi.CanWrite) {
296 // __ctrl.TemplateControl = this;
297 assign = new CodeAssignStatement ();
298 assign.Left = new CodePropertyReferenceExpression (ctrlVar, "TemplateControl");;
299 assign.Right = thisRef;
300 method.Statements.Add (assign);
304 // _ctrl.SkinID = $value
305 // _ctrl.ApplyStyleSheetSkin (this);
307 // the SkinID assignment needs to come
308 // before the call to
309 // ApplyStyleSheetSkin, for obvious
310 // reasons. We skip SkinID in
311 // CreateAssignStatementsFromAttributes
312 // below.
314 string skinid = builder.GetAttribute ("skinid");
315 if (!String.IsNullOrEmpty (skinid))
316 CreateAssignStatementFromAttribute (builder, "skinid");
318 if (typeof (WebControl).IsAssignableFrom (type)) {
319 CodeMethodInvokeExpression applyStyleSheetSkin = new CodeMethodInvokeExpression (ctrlVar, "ApplyStyleSheetSkin");
320 if (typeof (Page).IsAssignableFrom (parser.BaseType))
321 applyStyleSheetSkin.Parameters.Add (thisRef);
322 else
323 applyStyleSheetSkin.Parameters.Add (new CodePropertyReferenceExpression (thisRef, "Page"));
324 method.Statements.Add (applyStyleSheetSkin);
327 // Process template children before anything else
328 ProcessTemplateChildren (builder);
330 // process ID here. It should be set before any other attributes are
331 // assigned, since the control code may rely on ID being set. We
332 // skip ID in CreateAssignStatementsFromAttributes
333 string ctl_id = builder.GetAttribute ("id");
334 if (ctl_id != null && ctl_id.Length != 0)
335 CreateAssignStatementFromAttribute (builder, "id");
337 if (typeof (ContentPlaceHolder).IsAssignableFrom (type)) {
338 List <string> placeHolderIds = MasterPageContentPlaceHolders;
339 string cphID = builder.ID;
341 if (!placeHolderIds.Contains (cphID))
342 placeHolderIds.Add (cphID);
344 CodeConditionStatement condStatement;
346 // Add the __Template_* field
347 string templateField = "__Template_" + cphID;
348 CodeMemberField fld = new CodeMemberField (typeof (ITemplate), templateField);
349 fld.Attributes = MemberAttributes.Private;
350 mainClass.Members.Add (fld);
352 CodeFieldReferenceExpression templateID = new CodeFieldReferenceExpression ();
353 templateID.TargetObject = thisRef;
354 templateID.FieldName = templateField;
356 CreateContentPlaceHolderTemplateProperty (templateField, "Template_" + cphID);
358 // if ((this.ContentTemplates != null)) {
359 // this.__Template_$builder.ID = ((System.Web.UI.ITemplate)(this.ContentTemplates["$builder.ID"]));
360 // }
362 CodeFieldReferenceExpression contentTemplates = new CodeFieldReferenceExpression ();
363 contentTemplates.TargetObject = thisRef;
364 contentTemplates.FieldName = "ContentTemplates";
366 CodeIndexerExpression indexer = new CodeIndexerExpression ();
367 indexer.TargetObject = new CodePropertyReferenceExpression (thisRef, "ContentTemplates");
368 indexer.Indices.Add (new CodePrimitiveExpression (cphID));
370 assign = new CodeAssignStatement ();
371 assign.Left = templateID;
372 assign.Right = new CodeCastExpression (new CodeTypeReference (typeof (ITemplate)), indexer);
374 condStatement = new CodeConditionStatement (new CodeBinaryOperatorExpression (contentTemplates,
375 CodeBinaryOperatorType.IdentityInequality,
376 new CodePrimitiveExpression (null)),
377 assign);
379 method.Statements.Add (condStatement);
381 // if ((this.__Template_mainContent != null)) {
382 // this.__Template_mainContent.InstantiateIn(__ctrl);
383 // }
384 // and also set things up such that any additional code ends up in:
385 // else {
386 // ...
387 // }
389 CodeMethodReferenceExpression methodRef = new CodeMethodReferenceExpression ();
390 methodRef.TargetObject = templateID;
391 methodRef.MethodName = "InstantiateIn";
393 CodeMethodInvokeExpression instantiateInInvoke;
394 instantiateInInvoke = new CodeMethodInvokeExpression (methodRef, ctrlVar);
396 condStatement = new CodeConditionStatement (new CodeBinaryOperatorExpression (templateID,
397 CodeBinaryOperatorType.IdentityInequality,
398 new CodePrimitiveExpression (null)),
399 new CodeExpressionStatement (instantiateInInvoke));
400 method.Statements.Add (condStatement);
402 // this is the bit that causes the following stuff to end up in the else { }
403 builder.MethodStatements = condStatement.FalseStatements;
407 if (inBuildControlTree)
408 AddStatementsToInitMethodBottom (builder, method);
410 mainClass.Members.Add (method);
413 void ProcessTemplateChildren (ControlBuilder builder)
415 ArrayList templates = builder.TemplateChildren;
416 if (templates != null && templates.Count > 0) {
417 foreach (TemplateBuilder tb in templates) {
418 CreateControlTree (tb, true, false);
419 if (tb.BindingDirection == BindingDirection.TwoWay) {
420 string extractMethod = CreateExtractValuesMethod (tb);
421 AddBindableTemplateInvocation (builder, tb.TagName, tb.Method.Name, extractMethod);
422 } else
423 AddTemplateInvocation (builder, tb.TagName, tb.Method.Name);
428 void SetCustomAttribute (CodeMemberMethod method, UnknownAttributeDescriptor uad)
430 CodeAssignStatement assign = new CodeAssignStatement ();
431 assign.Left = new CodePropertyReferenceExpression (
432 new CodeArgumentReferenceExpression("__ctrl"),
433 uad.Info.Name);
434 assign.Right = GetExpressionFromString (uad.Value.GetType (), uad.Value.ToString (), uad.Info);
436 method.Statements.Add (assign);
439 void SetCustomAttributes (CodeMemberMethod method)
441 Type baseType = parser.BaseType;
442 if (baseType == null)
443 return;
445 List <UnknownAttributeDescriptor> attrs = parser.UnknownMainAttributes;
446 if (attrs == null || attrs.Count == 0)
447 return;
449 foreach (UnknownAttributeDescriptor uad in attrs)
450 SetCustomAttribute (method, uad);
453 protected virtual void AddStatementsToInitMethodTop (ControlBuilder builder, CodeMemberMethod method)
455 ClientIDMode? mode = parser.ClientIDMode;
456 if (mode.HasValue) {
457 var cimRef = new CodeTypeReferenceExpression (typeof (ClientIDMode));
458 cimRef.Type.Options = CodeTypeReferenceOptions.GlobalReference;
460 var assign = new CodeAssignStatement ();
461 assign.Left = new CodePropertyReferenceExpression (thisRef, "ClientIDMode");
462 assign.Right = new CodeFieldReferenceExpression (cimRef, mode.Value.ToString ());
464 method.Statements.Add (assign);
468 protected virtual void AddStatementsToInitMethodBottom (ControlBuilder builder, CodeMemberMethod method)
472 void AddLiteralSubObject (ControlBuilder builder, string str)
474 if (!builder.HasAspCode) {
475 CodeObjectCreateExpression expr;
476 expr = new CodeObjectCreateExpression (typeof (LiteralControl), new CodePrimitiveExpression (str));
477 AddParsedSubObjectStmt (builder, expr);
478 } else {
479 CodeMethodReferenceExpression methodRef = new CodeMethodReferenceExpression ();
480 methodRef.TargetObject = new CodeArgumentReferenceExpression ("__output");
481 methodRef.MethodName = "Write";
483 CodeMethodInvokeExpression expr;
484 expr = new CodeMethodInvokeExpression (methodRef, new CodePrimitiveExpression (str));
485 builder.RenderMethod.Statements.Add (expr);
489 string TrimDB (string value, bool trimTail)
491 string str = value.Trim ();
492 int len = str.Length;
493 int idx = str.IndexOf ('#', 2) + 1;
494 if (idx >= len)
495 return String.Empty;
496 if (trimTail)
497 len -= 2;
499 return str.Substring (idx, len - idx).Trim ();
502 CodeExpression CreateEvalInvokeExpression (Regex regex, string value, bool isBind)
504 Match match = regex.Match (value);
505 if (!match.Success) {
506 if (isBind)
507 throw new HttpParseException ("Bind invocation wasn't formatted properly.");
508 return null;
511 string sanitizedSnippet;
512 if (isBind)
513 sanitizedSnippet = SanitizeBindCall (match);
514 else
515 sanitizedSnippet = value;
517 return new CodeSnippetExpression (sanitizedSnippet);
520 string SanitizeBindCall (Match match)
522 GroupCollection groups = match.Groups;
523 StringBuilder sb = new StringBuilder ("Eval(\"" + groups [1] + "\"");
524 Group second = groups [4];
525 if (second != null) {
526 string v = second.Value;
527 if (v != null && v.Length > 0)
528 sb.Append (",\"" + second + "\"");
531 sb.Append (")");
532 return sb.ToString ();
535 string DataBoundProperty (ControlBuilder builder, Type type, string varName, string value)
537 value = TrimDB (value, true);
538 CodeMemberMethod method;
539 string dbMethodName = builder.Method.Name + "_DB_" + dataBoundAtts++;
540 CodeExpression valueExpression = null;
541 value = value.Trim ();
543 bool need_if = false;
544 if (startsWithBindRegex.Match (value).Success) {
545 valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, true);
546 if (valueExpression != null)
547 need_if = true;
548 } else
549 if (StrUtils.StartsWith (value, "Eval", true))
550 valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, false);
552 if (valueExpression == null)
553 valueExpression = new CodeSnippetExpression (value);
555 method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType);
556 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
558 // This should be a CodePropertyReferenceExpression for properties... but it works anyway
559 CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (targetExpr, varName);
561 CodeExpression expr;
562 if (type == typeof (string)) {
563 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
564 CodeTypeReferenceExpression conv = new CodeTypeReferenceExpression (typeof (Convert));
565 tostring.Method = new CodeMethodReferenceExpression (conv, "ToString");
566 tostring.Parameters.Add (valueExpression);
567 expr = tostring;
568 } else
569 expr = new CodeCastExpression (type, valueExpression);
571 CodeAssignStatement assign = new CodeAssignStatement (field, expr);
572 if (need_if) {
573 CodeExpression page = new CodePropertyReferenceExpression (thisRef, "Page");
574 CodeExpression left = new CodeMethodInvokeExpression (page, "GetDataItem");
575 CodeBinaryOperatorExpression ce = new CodeBinaryOperatorExpression (left, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
576 CodeConditionStatement ccs = new CodeConditionStatement (ce, assign);
577 method.Statements.Add (ccs);
578 } else
579 method.Statements.Add (assign);
581 mainClass.Members.Add (method);
582 return method.Name;
585 void AddCodeForPropertyOrField (ControlBuilder builder, Type type, string var_name, string att, MemberInfo member, bool isDataBound, bool isExpression)
587 CodeMemberMethod method = builder.Method;
588 bool isWritable = IsWritablePropertyOrField (member);
590 if (isDataBound && isWritable) {
591 string dbMethodName = DataBoundProperty (builder, type, var_name, att);
592 AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
593 return;
594 } else if (isExpression && isWritable) {
595 AddExpressionAssign (method, builder, member, type, var_name, att);
596 return;
599 CodeAssignStatement assign = new CodeAssignStatement ();
600 assign.Left = new CodePropertyReferenceExpression (ctrlVar, var_name);
601 currentLocation = builder.Location;
602 assign.Right = GetExpressionFromString (type, att, member);
604 method.Statements.Add (AddLinePragma (assign, builder));
607 void RegisterBindingInfo (ControlBuilder builder, string propName, ref string value)
609 string str = TrimDB (value, false);
610 if (StrUtils.StartsWith (str, "Bind", true)) {
611 Match match = bindRegex.Match (str);
612 if (match.Success) {
613 string bindingName = match.Groups [1].Value;
614 TemplateBuilder templateBuilder = builder.ParentTemplateBuilder;
616 if (templateBuilder == null)
617 throw new HttpException ("Bind expression not allowed in this context.");
619 if (templateBuilder.BindingDirection == BindingDirection.OneWay)
620 return;
622 string id = builder.GetAttribute ("ID");
623 if (String.IsNullOrEmpty (id))
624 throw new HttpException ("Control of type '" + builder.ControlType + "' using two-way binding on property '" + propName + "' must have an ID.");
626 templateBuilder.RegisterBoundProperty (builder.ControlType, propName, id, bindingName);
632 static bool InvariantCompare (string a, string b)
634 return (0 == String.Compare (a, b, false, Helpers.InvariantCulture));
638 static bool InvariantCompareNoCase (string a, string b)
640 return (0 == String.Compare (a, b, true, Helpers.InvariantCulture));
643 internal static MemberInfo GetFieldOrProperty (Type type, string name)
645 MemberInfo member = null;
646 try {
647 member = type.GetProperty (name, noCaseFlags & ~BindingFlags.NonPublic);
648 } catch {}
650 if (member != null)
651 return member;
653 try {
654 member = type.GetField (name, noCaseFlags & ~BindingFlags.NonPublic);
655 } catch {}
657 return member;
660 static bool IsWritablePropertyOrField (MemberInfo member)
662 PropertyInfo pi = member as PropertyInfo;
663 if (pi != null)
664 return pi.GetSetMethod (false) != null;
665 FieldInfo fi = member as FieldInfo;
666 if (fi != null)
667 return !fi.IsInitOnly;
668 throw new ArgumentException ("Argument must be of PropertyInfo or FieldInfo type", "member");
671 bool ProcessPropertiesAndFields (ControlBuilder builder, MemberInfo member, string id,
672 string attValue, string prefix)
674 int hyphen = id.IndexOf ('-');
675 bool isPropertyInfo = (member is PropertyInfo);
676 bool isDataBound = BaseParser.IsDataBound (attValue);
677 bool isExpression = !isDataBound && BaseParser.IsExpression (attValue);
678 Type type;
679 if (isPropertyInfo) {
680 type = ((PropertyInfo) member).PropertyType;
681 } else {
682 type = ((FieldInfo) member).FieldType;
685 if (InvariantCompareNoCase (member.Name, id)) {
686 if (isDataBound)
687 RegisterBindingInfo (builder, member.Name, ref attValue);
689 if (!IsWritablePropertyOrField (member))
690 return false;
692 AddCodeForPropertyOrField (builder, type, member.Name, attValue, member, isDataBound, isExpression);
693 return true;
696 if (hyphen == -1)
697 return false;
699 string prop_field = id.Replace ('-', '.');
700 string [] parts = prop_field.Split (new char [] {'.'});
701 int length = parts.Length;
703 if (length < 2 || !InvariantCompareNoCase (member.Name, parts [0]))
704 return false;
706 if (length > 2) {
707 MemberInfo sub_member = GetFieldOrProperty (type, parts [1]);
708 if (sub_member == null)
709 return false;
711 string new_prefix = prefix + member.Name + ".";
712 string new_id = id.Substring (hyphen + 1);
713 return ProcessPropertiesAndFields (builder, sub_member, new_id, attValue, new_prefix);
716 MemberInfo subpf = GetFieldOrProperty (type, parts [1]);
717 if (!(subpf is PropertyInfo))
718 return false;
720 PropertyInfo subprop = (PropertyInfo) subpf;
721 if (subprop.CanWrite == false)
722 return false;
724 bool is_bool = (subprop.PropertyType == typeof (bool));
725 if (!is_bool && attValue == null)
726 return false; // Font-Size -> Font-Size="" as html
728 string val = attValue;
729 if (attValue == null && is_bool)
730 val = "true"; // Font-Bold <=> Font-Bold="true"
732 if (isDataBound)
733 RegisterBindingInfo (builder, prefix + member.Name + "." + subprop.Name, ref attValue);
735 AddCodeForPropertyOrField (builder, subprop.PropertyType,
736 prefix + member.Name + "." + subprop.Name,
737 val, subprop, isDataBound, isExpression);
739 return true;
742 internal CodeExpression CompileExpression (MemberInfo member, Type type, string value, bool useSetAttribute)
744 // First let's find the correct expression builder
745 value = value.Substring (3, value.Length - 5).Trim ();
746 int colon = value.IndexOf (':');
747 if (colon == -1)
748 return null;
749 string prefix = value.Substring (0, colon).Trim ();
750 string expr = value.Substring (colon + 1).Trim ();
752 CompilationSection cs = (CompilationSection)WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
753 if (cs == null)
754 return null;
756 if (cs.ExpressionBuilders == null || cs.ExpressionBuilders.Count == 0)
757 return null;
759 System.Web.Configuration.ExpressionBuilder ceb = cs.ExpressionBuilders[prefix];
760 if (ceb == null)
761 return null;
763 string builderType = ceb.Type;
764 Type t;
766 try {
767 t = HttpApplication.LoadType (builderType, true);
768 } catch (Exception e) {
769 throw new HttpException (String.Format ("Failed to load expression builder type `{0}'", builderType), e);
772 if (!typeof (System.Web.Compilation.ExpressionBuilder).IsAssignableFrom (t))
773 throw new HttpException (String.Format ("Type {0} is not descendant from System.Web.Compilation.ExpressionBuilder", builderType));
775 System.Web.Compilation.ExpressionBuilder eb = null;
776 object parsedData;
777 ExpressionBuilderContext ctx;
779 try {
780 eb = Activator.CreateInstance (t) as System.Web.Compilation.ExpressionBuilder;
781 ctx = new ExpressionBuilderContext (HttpContext.Current.Request.FilePath);
782 parsedData = eb.ParseExpression (expr, type, ctx);
783 } catch (Exception e) {
784 throw new HttpException (String.Format ("Failed to create an instance of type `{0}'", builderType), e);
787 BoundPropertyEntry bpe = CreateBoundPropertyEntry (member as PropertyInfo, prefix, expr, useSetAttribute);
788 return eb.GetCodeExpression (bpe, parsedData, ctx);
791 void AddExpressionAssign (CodeMemberMethod method, ControlBuilder builder, MemberInfo member, Type type, string name, string value)
793 CodeExpression expr = CompileExpression (member, type, value, false);
795 if (expr == null)
796 return;
798 CodeAssignStatement assign = new CodeAssignStatement ();
799 assign.Left = new CodePropertyReferenceExpression (ctrlVar, name);
801 TypeCode typeCode = Type.GetTypeCode (type);
802 if (typeCode != TypeCode.Empty && typeCode != TypeCode.Object && typeCode != TypeCode.DBNull)
803 assign.Right = CreateConvertToCall (typeCode, expr);
804 else
805 assign.Right = new CodeCastExpression (type, expr);
807 builder.Method.Statements.Add (AddLinePragma (assign, builder));
810 internal static CodeMethodInvokeExpression CreateConvertToCall (TypeCode typeCode, CodeExpression expr)
812 var ret = new CodeMethodInvokeExpression ();
813 string methodName;
815 switch (typeCode) {
816 case TypeCode.Boolean:
817 methodName = "ToBoolean";
818 break;
820 case TypeCode.Char:
821 methodName = "ToChar";
822 break;
824 case TypeCode.SByte:
825 methodName = "ToSByte";
826 break;
828 case TypeCode.Byte:
829 methodName = "ToByte";
830 break;
832 case TypeCode.Int16:
833 methodName = "ToInt16";
834 break;
836 case TypeCode.UInt16:
837 methodName = "ToUInt16";
838 break;
840 case TypeCode.Int32:
841 methodName = "ToInt32";
842 break;
844 case TypeCode.UInt32:
845 methodName = "ToUInt32";
846 break;
848 case TypeCode.Int64:
849 methodName = "ToInt64";
850 break;
852 case TypeCode.UInt64:
853 methodName = "ToUInt64";
854 break;
856 case TypeCode.Single:
857 methodName = "ToSingle";
858 break;
860 case TypeCode.Double:
861 methodName = "ToDouble";
862 break;
864 case TypeCode.Decimal:
865 methodName = "ToDecimal";
866 break;
868 case TypeCode.DateTime:
869 methodName = "ToDateTime";
870 break;
872 case TypeCode.String:
873 methodName = "ToString";
874 break;
876 default:
877 throw new InvalidOperationException (String.Format ("Unsupported TypeCode '{0}'", typeCode));
880 var typeRef = new CodeTypeReferenceExpression (typeof (Convert));
881 typeRef.Type.Options = CodeTypeReferenceOptions.GlobalReference;
883 ret.Method = new CodeMethodReferenceExpression (typeRef, methodName);
884 ret.Parameters.Add (expr);
885 ret.Parameters.Add (new CodePropertyReferenceExpression (new CodeTypeReferenceExpression (typeof (System.Globalization.CultureInfo)), "CurrentCulture"));
887 return ret;
890 BoundPropertyEntry CreateBoundPropertyEntry (PropertyInfo pi, string prefix, string expr, bool useSetAttribute)
892 BoundPropertyEntry ret = new BoundPropertyEntry ();
893 ret.Expression = expr;
894 ret.ExpressionPrefix = prefix;
895 ret.Generated = false;
896 if (pi != null) {
897 ret.Name = pi.Name;
898 ret.PropertyInfo = pi;
899 ret.Type = pi.PropertyType;
901 ret.UseSetAttribute = useSetAttribute;
903 return ret;
906 bool ResourceProviderHasObject (string key)
908 IResourceProvider rp = HttpContext.GetResourceProvider (InputVirtualPath.Absolute, true);
909 if (rp == null)
910 return false;
912 IResourceReader rr = rp.ResourceReader;
913 if (rr == null)
914 return false;
916 try {
917 IDictionaryEnumerator ide = rr.GetEnumerator ();
918 if (ide == null)
919 return false;
921 string dictKey;
922 while (ide.MoveNext ()) {
923 dictKey = ide.Key as string;
924 if (String.IsNullOrEmpty (dictKey))
925 continue;
926 if (String.Compare (key, dictKey, StringComparison.Ordinal) == 0)
927 return true;
929 } finally {
930 rr.Close ();
933 return false;
936 void AssignPropertyFromResources (ControlBuilder builder, MemberInfo mi, string attvalue)
938 bool isProperty = mi.MemberType == MemberTypes.Property;
939 bool isField = !isProperty && (mi.MemberType == MemberTypes.Field);
941 if (!isProperty && !isField || !IsWritablePropertyOrField (mi))
942 return;
944 object[] attrs = mi.GetCustomAttributes (typeof (LocalizableAttribute), true);
945 if (attrs != null && attrs.Length > 0 && !((LocalizableAttribute)attrs [0]).IsLocalizable)
946 return;
948 string memberName = mi.Name;
949 string resname = String.Concat (attvalue, ".", memberName);
951 if (!ResourceProviderHasObject (resname))
952 return;
954 // __ctrl.Text = System.Convert.ToString(HttpContext.GetLocalResourceObject("ButtonResource1.Text"));
955 string inputFile = parser.InputFile;
956 string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
958 if (StrUtils.StartsWith (inputFile, physPath)) {
959 string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
960 inputFile = parser.InputFile.Substring (physPath.Length - 1);
961 if (appVirtualPath != "/")
962 inputFile = appVirtualPath + inputFile;
963 } else
964 return;
966 char dsc = System.IO.Path.DirectorySeparatorChar;
967 if (dsc != '/')
968 inputFile = inputFile.Replace (dsc, '/');
970 object obj = HttpContext.GetLocalResourceObject (inputFile, resname);
971 if (obj == null)
972 return;
974 if (!isProperty && !isField)
975 return; // an "impossible" case
977 CodeAssignStatement assign = new CodeAssignStatement ();
979 assign.Left = new CodePropertyReferenceExpression (ctrlVar, memberName);
980 assign.Right = ResourceExpressionBuilder.CreateGetLocalResourceObject (mi, resname);
982 builder.Method.Statements.Add (AddLinePragma (assign, builder));
985 void AssignPropertiesFromResources (ControlBuilder builder, Type controlType, string attvalue)
987 // Process all public fields and properties of the control. We don't use GetMembers to make the code
988 // faster
989 FieldInfo [] fields = controlType.GetFields (
990 BindingFlags.Instance | BindingFlags.Static |
991 BindingFlags.Public | BindingFlags.FlattenHierarchy);
992 PropertyInfo [] properties = controlType.GetProperties (
993 BindingFlags.Instance | BindingFlags.Static |
994 BindingFlags.Public | BindingFlags.FlattenHierarchy);
996 foreach (FieldInfo fi in fields)
997 AssignPropertyFromResources (builder, fi, attvalue);
998 foreach (PropertyInfo pi in properties)
999 AssignPropertyFromResources (builder, pi, attvalue);
1002 void AssignPropertiesFromResources (ControlBuilder builder, string attvalue)
1004 if (attvalue == null || attvalue.Length == 0)
1005 return;
1007 Type controlType = builder.ControlType;
1008 if (controlType == null)
1009 return;
1011 AssignPropertiesFromResources (builder, controlType, attvalue);
1014 void AddEventAssign (CodeMemberMethod method, ControlBuilder builder, string name, Type type, string value)
1016 //"__ctrl.{0} += new {1} (this.{2});"
1017 CodeEventReferenceExpression evtID = new CodeEventReferenceExpression (ctrlVar, name);
1019 CodeDelegateCreateExpression create;
1020 create = new CodeDelegateCreateExpression (new CodeTypeReference (type), thisRef, value);
1022 CodeAttachEventStatement attach = new CodeAttachEventStatement (evtID, create);
1023 method.Statements.Add (attach);
1026 void CreateAssignStatementFromAttribute (ControlBuilder builder, string id)
1028 EventInfo [] ev_info = null;
1029 Type type = builder.ControlType;
1031 string attvalue = builder.GetAttribute (id);
1032 if (id.Length > 2 && String.Compare (id.Substring (0, 2), "ON", true, Helpers.InvariantCulture) == 0){
1033 if (ev_info == null)
1034 ev_info = type.GetEvents ();
1036 string id_as_event = id.Substring (2);
1037 foreach (EventInfo ev in ev_info){
1038 if (InvariantCompareNoCase (ev.Name, id_as_event)){
1039 AddEventAssign (builder.Method,
1040 builder,
1041 ev.Name,
1042 ev.EventHandlerType,
1043 attvalue);
1045 return;
1051 if (String.Compare (id, "meta:resourcekey", StringComparison.OrdinalIgnoreCase) == 0) {
1052 AssignPropertiesFromResources (builder, attvalue);
1053 return;
1056 int hyphen = id.IndexOf ('-');
1057 string alt_id = id;
1058 if (hyphen != -1)
1059 alt_id = id.Substring (0, hyphen);
1061 MemberInfo fop = GetFieldOrProperty (type, alt_id);
1062 if (fop != null) {
1063 if (ProcessPropertiesAndFields (builder, fop, id, attvalue, null))
1064 return;
1067 if (!typeof (IAttributeAccessor).IsAssignableFrom (type))
1068 throw new ParseException (builder.Location, "Unrecognized attribute: " + id);
1070 CodeMemberMethod method = builder.Method;
1071 bool isDatabound = BaseParser.IsDataBound (attvalue);
1072 bool isExpression = !isDatabound && BaseParser.IsExpression (attvalue);
1074 if (isDatabound) {
1075 string value = attvalue.Substring (3, attvalue.Length - 5).Trim ();
1076 CodeExpression valueExpression = null;
1077 if (startsWithBindRegex.Match (value).Success)
1078 valueExpression = CreateEvalInvokeExpression (bindRegexInValue, value, true);
1079 else
1080 if (StrUtils.StartsWith (value, "Eval", true))
1081 valueExpression = CreateEvalInvokeExpression (evalRegexInValue, value, false);
1083 if (valueExpression == null && value != null && value.Trim () != String.Empty)
1084 valueExpression = new CodeSnippetExpression (value);
1086 CreateDBAttributeMethod (builder, id, valueExpression);
1087 } else {
1088 CodeCastExpression cast;
1089 CodeMethodReferenceExpression methodExpr;
1090 CodeMethodInvokeExpression expr;
1092 cast = new CodeCastExpression (typeof (IAttributeAccessor), ctrlVar);
1093 methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
1094 expr = new CodeMethodInvokeExpression (methodExpr);
1095 expr.Parameters.Add (new CodePrimitiveExpression (id));
1097 CodeExpression valueExpr = null;
1098 if (isExpression)
1099 valueExpr = CompileExpression (null, typeof (string), attvalue, true);
1101 if (valueExpr == null)
1102 valueExpr = new CodePrimitiveExpression (attvalue);
1104 expr.Parameters.Add (valueExpr);
1105 method.Statements.Add (AddLinePragma (expr, builder));
1109 protected void CreateAssignStatementsFromAttributes (ControlBuilder builder)
1111 this.dataBoundAtts = 0;
1112 IDictionary atts = builder.Attributes;
1113 if (atts == null || atts.Count == 0)
1114 return;
1116 foreach (string id in atts.Keys) {
1117 if (InvariantCompareNoCase (id, "runat"))
1118 continue;
1119 // ID is assigned in BuildControltree
1120 if (InvariantCompareNoCase (id, "id"))
1121 continue;
1123 /* we skip SkinID here as it's assigned in BuildControlTree */
1124 if (InvariantCompareNoCase (id, "skinid"))
1125 continue;
1126 if (InvariantCompareNoCase (id, "meta:resourcekey"))
1127 continue; // ignore, this one's processed at the very end of
1128 // the method
1129 CreateAssignStatementFromAttribute (builder, id);
1133 void CreateDBAttributeMethod (ControlBuilder builder, string attr, CodeExpression code)
1135 if (code == null)
1136 return;
1138 string id = builder.GetNextID (null);
1139 string dbMethodName = "__DataBind_" + id;
1140 CodeMemberMethod method = builder.Method;
1141 AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
1143 method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), builder.ControlType);
1144 builder.DataBindingMethod = method;
1146 CodeCastExpression cast;
1147 CodeMethodReferenceExpression methodExpr;
1148 CodeMethodInvokeExpression expr;
1150 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
1151 cast = new CodeCastExpression (typeof (IAttributeAccessor), targetExpr);
1152 methodExpr = new CodeMethodReferenceExpression (cast, "SetAttribute");
1153 expr = new CodeMethodInvokeExpression (methodExpr);
1154 expr.Parameters.Add (new CodePrimitiveExpression (attr));
1155 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
1156 tostring.Method = new CodeMethodReferenceExpression (
1157 new CodeTypeReferenceExpression (typeof (Convert)),
1158 "ToString");
1159 tostring.Parameters.Add (code);
1160 expr.Parameters.Add (tostring);
1161 method.Statements.Add (expr);
1162 mainClass.Members.Add (method);
1165 void AddRenderControl (ControlBuilder builder)
1167 CodeIndexerExpression indexer = new CodeIndexerExpression ();
1168 indexer.TargetObject = new CodePropertyReferenceExpression (
1169 new CodeArgumentReferenceExpression ("parameterContainer"),
1170 "Controls");
1172 indexer.Indices.Add (new CodePrimitiveExpression (builder.RenderIndex));
1174 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (indexer, "RenderControl");
1175 invoke.Parameters.Add (new CodeArgumentReferenceExpression ("__output"));
1176 builder.RenderMethod.Statements.Add (invoke);
1177 builder.IncreaseRenderIndex ();
1180 protected void AddChildCall (ControlBuilder parent, ControlBuilder child)
1182 if (parent == null || child == null)
1183 return;
1185 CodeStatementCollection methodStatements = parent.MethodStatements;
1186 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression (thisRef, child.Method.Name);
1187 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (m);
1189 object [] atts = null;
1191 if (child.ControlType != null)
1192 atts = child.ControlType.GetCustomAttributes (typeof (PartialCachingAttribute), true);
1194 if (atts != null && atts.Length > 0) {
1195 PartialCachingAttribute pca = (PartialCachingAttribute) atts [0];
1196 CodeTypeReferenceExpression cc = new CodeTypeReferenceExpression("System.Web.UI.StaticPartialCachingControl");
1197 CodeMethodInvokeExpression build = new CodeMethodInvokeExpression (cc, "BuildCachedControl");
1198 CodeExpressionCollection parms = build.Parameters;
1200 parms.Add (new CodeArgumentReferenceExpression("__ctrl"));
1201 parms.Add (new CodePrimitiveExpression (child.ID));
1203 if (pca.Shared)
1204 parms.Add (new CodePrimitiveExpression (child.ControlType.GetHashCode ().ToString ()));
1205 else
1206 parms.Add (new CodePrimitiveExpression (Guid.NewGuid ().ToString ()));
1208 parms.Add (new CodePrimitiveExpression (pca.Duration));
1209 parms.Add (new CodePrimitiveExpression (pca.VaryByParams));
1210 parms.Add (new CodePrimitiveExpression (pca.VaryByControls));
1211 parms.Add (new CodePrimitiveExpression (pca.VaryByCustom));
1212 parms.Add (new CodePrimitiveExpression (pca.SqlDependency));
1213 parms.Add (new CodeDelegateCreateExpression (
1214 new CodeTypeReference (typeof (System.Web.UI.BuildMethod)),
1215 thisRef, child.Method.Name));
1216 string value = pca.ProviderName;
1217 if (!String.IsNullOrEmpty (value) && String.Compare (OutputCache.DEFAULT_PROVIDER_NAME, value, StringComparison.Ordinal) != 0)
1218 parms.Add (new CodePrimitiveExpression (value));
1219 else
1220 parms.Add (new CodePrimitiveExpression (null));
1221 methodStatements.Add (AddLinePragma (build, parent));
1222 if (parent.HasAspCode)
1223 AddRenderControl (parent);
1224 return;
1227 if (child.IsProperty || parent.ChildrenAsProperties) {
1228 if (!child.PropertyBuilderShouldReturnValue) {
1229 expr.Parameters.Add (new CodeFieldReferenceExpression (ctrlVar, child.TagName));
1230 parent.MethodStatements.Add (AddLinePragma (expr, parent));
1231 } else {
1232 string localVarName = parent.GetNextLocalVariableName ("__ctrl");
1233 methodStatements.Add (new CodeVariableDeclarationStatement (child.Method.ReturnType, localVarName));
1234 CodeVariableReferenceExpression localVarRef = new CodeVariableReferenceExpression (localVarName);
1235 CodeAssignStatement assign = new CodeAssignStatement ();
1236 assign.Left = localVarRef;
1237 assign.Right = expr;
1238 methodStatements.Add (AddLinePragma (assign, parent));
1240 assign = new CodeAssignStatement ();
1241 assign.Left = new CodeFieldReferenceExpression (ctrlVar, child.TagName);
1242 assign.Right = localVarRef;
1243 methodStatements.Add (AddLinePragma (assign, parent));
1246 return;
1249 methodStatements.Add (AddLinePragma (expr, parent));
1250 CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, child.ID);
1251 if (parent.ControlType == null || typeof (IParserAccessor).IsAssignableFrom (parent.ControlType))
1252 AddParsedSubObjectStmt (parent, field);
1253 else {
1254 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (ctrlVar, "Add");
1255 invoke.Parameters.Add (field);
1256 methodStatements.Add (AddLinePragma (invoke, parent));
1259 if (parent.HasAspCode)
1260 AddRenderControl (parent);
1263 void AddTemplateInvocation (ControlBuilder builder, string name, string methodName)
1265 CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
1267 CodeDelegateCreateExpression newBuild = new CodeDelegateCreateExpression (
1268 new CodeTypeReference (typeof (BuildTemplateMethod)), thisRef, methodName);
1270 CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
1271 newCompiled.Parameters.Add (newBuild);
1273 CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
1274 builder.Method.Statements.Add (AddLinePragma (assign, builder));
1277 void AddBindableTemplateInvocation (ControlBuilder builder, string name, string methodName, string extractMethodName)
1279 CodePropertyReferenceExpression prop = new CodePropertyReferenceExpression (ctrlVar, name);
1281 CodeDelegateCreateExpression newBuild = new CodeDelegateCreateExpression (
1282 new CodeTypeReference (typeof (BuildTemplateMethod)), thisRef, methodName);
1284 CodeDelegateCreateExpression newExtract = new CodeDelegateCreateExpression (
1285 new CodeTypeReference (typeof (ExtractTemplateValuesMethod)), thisRef, extractMethodName);
1287 CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledBindableTemplateBuilder));
1288 newCompiled.Parameters.Add (newBuild);
1289 newCompiled.Parameters.Add (newExtract);
1291 CodeAssignStatement assign = new CodeAssignStatement (prop, newCompiled);
1292 builder.Method.Statements.Add (AddLinePragma (assign, builder));
1295 string CreateExtractValuesMethod (TemplateBuilder builder)
1297 CodeMemberMethod method = new CodeMemberMethod ();
1298 method.Name = "__ExtractValues_" + builder.ID;
1299 method.Attributes = MemberAttributes.Private | MemberAttributes.Final;
1300 method.ReturnType = new CodeTypeReference (typeof(IOrderedDictionary));
1302 CodeParameterDeclarationExpression arg = new CodeParameterDeclarationExpression ();
1303 arg.Type = new CodeTypeReference (typeof (Control));
1304 arg.Name = "__container";
1305 method.Parameters.Add (arg);
1306 mainClass.Members.Add (method);
1308 CodeObjectCreateExpression newTable = new CodeObjectCreateExpression ();
1309 newTable.CreateType = new CodeTypeReference (typeof(OrderedDictionary));
1310 method.Statements.Add (new CodeVariableDeclarationStatement (typeof(OrderedDictionary), "__table", newTable));
1311 CodeVariableReferenceExpression tableExp = new CodeVariableReferenceExpression ("__table");
1313 if (builder.Bindings != null) {
1314 Hashtable hash = new Hashtable ();
1315 foreach (TemplateBinding binding in builder.Bindings) {
1316 CodeConditionStatement sif;
1317 CodeVariableReferenceExpression control;
1318 CodeAssignStatement assign;
1320 if (hash [binding.ControlId] == null) {
1322 CodeVariableDeclarationStatement dec = new CodeVariableDeclarationStatement (binding.ControlType, binding.ControlId);
1323 method.Statements.Add (dec);
1324 CodeVariableReferenceExpression cter = new CodeVariableReferenceExpression ("__container");
1325 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (cter, "FindControl");
1326 invoke.Parameters.Add (new CodePrimitiveExpression (binding.ControlId));
1328 assign = new CodeAssignStatement ();
1329 control = new CodeVariableReferenceExpression (binding.ControlId);
1330 assign.Left = control;
1331 assign.Right = new CodeCastExpression (binding.ControlType, invoke);
1332 method.Statements.Add (assign);
1334 sif = new CodeConditionStatement ();
1335 sif.Condition = new CodeBinaryOperatorExpression (control, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression (null));
1337 method.Statements.Add (sif);
1339 hash [binding.ControlId] = sif;
1342 sif = (CodeConditionStatement) hash [binding.ControlId];
1343 control = new CodeVariableReferenceExpression (binding.ControlId);
1344 assign = new CodeAssignStatement ();
1345 assign.Left = new CodeIndexerExpression (tableExp, new CodePrimitiveExpression (binding.FieldName));
1346 assign.Right = new CodePropertyReferenceExpression (control, binding.ControlProperty);
1347 sif.TrueStatements.Add (assign);
1351 method.Statements.Add (new CodeMethodReturnStatement (tableExp));
1352 return method.Name;
1355 void AddContentTemplateInvocation (ContentBuilderInternal cbuilder, CodeMemberMethod method, string methodName)
1357 CodeDelegateCreateExpression newBuild = new CodeDelegateCreateExpression (
1358 new CodeTypeReference (typeof (BuildTemplateMethod)), thisRef, methodName);
1360 CodeObjectCreateExpression newCompiled = new CodeObjectCreateExpression (typeof (CompiledTemplateBuilder));
1361 newCompiled.Parameters.Add (newBuild);
1363 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (thisRef, "AddContentTemplate");
1364 invoke.Parameters.Add (new CodePrimitiveExpression (cbuilder.ContentPlaceHolderID));
1365 invoke.Parameters.Add (newCompiled);
1367 method.Statements.Add (AddLinePragma (invoke, cbuilder));
1370 void AddCodeRender (ControlBuilder parent, CodeRenderBuilder cr)
1372 if (cr.Code == null || cr.Code.Trim () == "")
1373 return;
1375 if (!cr.IsAssign) {
1376 CodeSnippetStatement code = new CodeSnippetStatement (cr.Code);
1377 parent.RenderMethod.Statements.Add (AddLinePragma (code, cr));
1378 return;
1381 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression ();
1382 expr.Method = new CodeMethodReferenceExpression (
1383 new CodeArgumentReferenceExpression ("__output"),
1384 "Write");
1386 expr.Parameters.Add (GetWrappedCodeExpression (cr));
1387 parent.RenderMethod.Statements.Add (AddLinePragma (expr, cr));
1390 CodeExpression GetWrappedCodeExpression (CodeRenderBuilder cr)
1392 var ret = new CodeSnippetExpression (cr.Code);
1393 if (cr.HtmlEncode) {
1394 var encodeRef = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (HttpUtility)), "HtmlEncode");
1395 return new CodeMethodInvokeExpression (encodeRef, new CodeExpression[] { ret });
1396 } else
1397 return ret;
1400 static Type GetContainerType (ControlBuilder builder)
1402 return builder.BindingContainerType;
1405 CodeMemberMethod CreateDBMethod (ControlBuilder builder, string name, Type container, Type target)
1407 CodeMemberMethod method = new CodeMemberMethod ();
1408 method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
1409 method.Name = name;
1410 method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (object), "sender"));
1411 method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (EventArgs), "e"));
1413 CodeTypeReference containerRef = new CodeTypeReference (container);
1414 CodeTypeReference targetRef = new CodeTypeReference (target);
1416 CodeVariableDeclarationStatement decl = new CodeVariableDeclarationStatement();
1417 decl.Name = "Container";
1418 decl.Type = containerRef;
1419 method.Statements.Add (decl);
1421 decl = new CodeVariableDeclarationStatement();
1422 decl.Name = "target";
1423 decl.Type = targetRef;
1424 method.Statements.Add (decl);
1426 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
1427 CodeAssignStatement assign = new CodeAssignStatement ();
1428 assign.Left = targetExpr;
1429 assign.Right = new CodeCastExpression (targetRef, new CodeArgumentReferenceExpression ("sender"));
1430 method.Statements.Add (AddLinePragma (assign, builder));
1432 assign = new CodeAssignStatement ();
1433 assign.Left = new CodeVariableReferenceExpression ("Container");
1434 assign.Right = new CodeCastExpression (containerRef,
1435 new CodePropertyReferenceExpression (targetExpr, "BindingContainer"));
1436 method.Statements.Add (AddLinePragma (assign, builder));
1438 return method;
1441 void AddDataBindingLiteral (ControlBuilder builder, DataBindingBuilder db)
1443 if (db.Code == null || db.Code.Trim () == "")
1444 return;
1446 EnsureID (db);
1447 CreateField (db, false);
1449 string dbMethodName = "__DataBind_" + db.ID;
1450 // Add the method that builds the DataBoundLiteralControl
1451 InitMethod (db, false, false);
1452 CodeMemberMethod method = db.Method;
1453 AddEventAssign (method, builder, "DataBinding", typeof (EventHandler), dbMethodName);
1454 method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
1456 // Add the DataBind handler
1457 method = CreateDBMethod (builder, dbMethodName, GetContainerType (builder), typeof (DataBoundLiteralControl));
1458 builder.DataBindingMethod = method;
1460 CodeVariableReferenceExpression targetExpr = new CodeVariableReferenceExpression ("target");
1461 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
1462 invoke.Method = new CodeMethodReferenceExpression (targetExpr, "SetDataBoundString");
1463 invoke.Parameters.Add (new CodePrimitiveExpression (0));
1465 CodeMethodInvokeExpression tostring = new CodeMethodInvokeExpression ();
1466 tostring.Method = new CodeMethodReferenceExpression (
1467 new CodeTypeReferenceExpression (typeof (Convert)),
1468 "ToString");
1469 tostring.Parameters.Add (new CodeSnippetExpression (db.Code));
1470 invoke.Parameters.Add (tostring);
1471 method.Statements.Add (AddLinePragma (invoke, builder));
1473 mainClass.Members.Add (method);
1475 AddChildCall (builder, db);
1478 void FlushText (ControlBuilder builder, StringBuilder sb)
1480 if (sb.Length > 0) {
1481 AddLiteralSubObject (builder, sb.ToString ());
1482 sb.Length = 0;
1486 protected void CreateControlTree (ControlBuilder builder, bool inTemplate, bool childrenAsProperties)
1488 EnsureID (builder);
1489 bool isTemplate = builder.IsTemplate;
1491 if (!isTemplate && !inTemplate) {
1492 CreateField (builder, true);
1493 } else if (!isTemplate) {
1494 bool doCheck = false;
1495 bool singleInstance = false;
1496 ControlBuilder pb = builder.ParentBuilder;
1497 TemplateBuilder tpb;
1498 while (pb != null) {
1499 tpb = pb as TemplateBuilder;
1500 if (tpb == null) {
1501 pb = pb.ParentBuilder;
1502 continue;
1505 if (tpb.TemplateInstance == TemplateInstance.Single)
1506 singleInstance = true;
1507 break;
1510 if (!singleInstance)
1511 builder.ID = builder.GetNextID (null);
1512 else
1513 doCheck = true;
1515 CreateField (builder, doCheck);
1518 InitMethod (builder, isTemplate, childrenAsProperties);
1519 if (!isTemplate || builder.GetType () == typeof (RootBuilder))
1520 CreateAssignStatementsFromAttributes (builder);
1522 if (builder.Children != null && builder.Children.Count > 0) {
1523 StringBuilder sb = new StringBuilder ();
1524 foreach (object b in builder.Children) {
1525 if (b is string) {
1526 sb.Append ((string) b);
1527 continue;
1530 FlushText (builder, sb);
1531 if (b is ObjectTagBuilder) {
1532 ProcessObjectTag ((ObjectTagBuilder) b);
1533 } else if (b is StringPropertyBuilder) {
1534 StringPropertyBuilder pb = b as StringPropertyBuilder;
1535 if (pb.Children != null && pb.Children.Count > 0) {
1536 StringBuilder asb = new StringBuilder ();
1537 foreach (string s in pb.Children)
1538 asb.Append (s);
1539 CodeMemberMethod method = builder.Method;
1540 CodeAssignStatement assign = new CodeAssignStatement ();
1541 assign.Left = new CodePropertyReferenceExpression (ctrlVar, pb.PropertyName);
1542 assign.Right = new CodePrimitiveExpression (asb.ToString ());
1543 method.Statements.Add (AddLinePragma (assign, builder));
1545 } else if (b is ContentBuilderInternal) {
1546 ContentBuilderInternal cb = (ContentBuilderInternal) b;
1547 CreateControlTree (cb, false, true);
1548 AddContentTemplateInvocation (cb, builder.Method, cb.Method.Name);
1549 continue;
1552 // Ignore TemplateBuilders - they are processed in InitMethod
1553 else if (b is TemplateBuilder) {
1554 } else if (b is CodeRenderBuilder) {
1555 AddCodeRender (builder, (CodeRenderBuilder) b);
1556 } else if (b is DataBindingBuilder) {
1557 AddDataBindingLiteral (builder, (DataBindingBuilder) b);
1558 } else if (b is ControlBuilder) {
1559 ControlBuilder child = (ControlBuilder) b;
1560 CreateControlTree (child, inTemplate, builder.ChildrenAsProperties);
1561 AddChildCall (builder, child);
1562 continue;
1563 } else
1564 throw new Exception ("???");
1566 ControlBuilder bldr = b as ControlBuilder;
1567 bldr.ProcessGeneratedCode (CompileUnit, BaseType, DerivedType, bldr.Method, bldr.DataBindingMethod);
1570 FlushText (builder, sb);
1573 ControlBuilder defaultPropertyBuilder = builder.DefaultPropertyBuilder;
1574 if (defaultPropertyBuilder != null) {
1575 CreateControlTree (defaultPropertyBuilder, false, true);
1576 AddChildCall (builder, defaultPropertyBuilder);
1579 if (builder.HasAspCode) {
1580 CodeMemberMethod renderMethod = builder.RenderMethod;
1581 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
1582 m.TargetObject = thisRef;
1583 m.MethodName = renderMethod.Name;
1585 CodeDelegateCreateExpression create = new CodeDelegateCreateExpression ();
1586 create.DelegateType = new CodeTypeReference (typeof (RenderMethod));
1587 create.TargetObject = thisRef;
1588 create.MethodName = renderMethod.Name;
1590 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression ();
1591 invoke.Method = new CodeMethodReferenceExpression (ctrlVar, "SetRenderMethodDelegate");
1592 invoke.Parameters.Add (create);
1594 builder.MethodStatements.Add (invoke);
1597 if (builder is RootBuilder)
1598 if (!String.IsNullOrEmpty (parser.MetaResourceKey))
1599 AssignPropertiesFromResources (builder, parser.BaseType, parser.MetaResourceKey);
1601 if ((!isTemplate || builder is RootBuilder) && !String.IsNullOrEmpty (builder.GetAttribute ("meta:resourcekey")))
1602 CreateAssignStatementFromAttribute (builder, "meta:resourcekey");
1604 if ((childrenAsProperties && builder.PropertyBuilderShouldReturnValue) || (!childrenAsProperties && typeof (Control).IsAssignableFrom (builder.ControlType)))
1605 builder.Method.Statements.Add (new CodeMethodReturnStatement (ctrlVar));
1607 builder.ProcessGeneratedCode (CompileUnit, BaseType, DerivedType, builder.Method, builder.DataBindingMethod);
1610 protected override void AddStatementsToConstructor (CodeConstructor ctor)
1612 if (masterPageContentPlaceHolders == null || masterPageContentPlaceHolders.Count == 0)
1613 return;
1615 var ilist = new CodeVariableDeclarationStatement ();
1616 ilist.Name = "__contentPlaceHolders";
1617 ilist.Type = new CodeTypeReference (typeof (IList));
1618 ilist.InitExpression = new CodePropertyReferenceExpression (thisRef, "ContentPlaceHolders");
1620 var ilistRef = new CodeVariableReferenceExpression ("__contentPlaceHolders");
1621 CodeStatementCollection statements = ctor.Statements;
1622 statements.Add (ilist);
1624 CodeMethodInvokeExpression mcall;
1625 foreach (string id in masterPageContentPlaceHolders) {
1626 mcall = new CodeMethodInvokeExpression (ilistRef, "Add");
1627 mcall.Parameters.Add (new CodePrimitiveExpression (id.ToLowerInvariant ()));
1628 statements.Add (mcall);
1632 protected internal override void CreateMethods ()
1634 base.CreateMethods ();
1636 CreateProperties ();
1637 CreateControlTree (parser.RootBuilder, false, false);
1638 CreateFrameworkInitializeMethod ();
1641 protected override void InitializeType ()
1643 List <string> registeredTagNames = parser.RegisteredTagNames;
1644 RootBuilder rb = parser.RootBuilder;
1645 if (rb == null || registeredTagNames == null || registeredTagNames.Count == 0)
1646 return;
1648 AspComponent component;
1649 foreach (string tagName in registeredTagNames) {
1650 component = rb.Foundry.GetComponent (tagName);
1651 if (component == null || component.Type == null) // unlikely
1652 throw new HttpException ("Custom control '" + tagName + "' cannot be found.");
1653 if (!(typeof (UserControl).IsAssignableFrom (component.Type)))
1654 throw new ParseException (parser.Location, "Type '" + component.Type.ToString () + "' does not derive from 'System.Web.UI.UserControl'.");
1655 AddReferencedAssembly (component.Type.Assembly);
1659 void CallBaseFrameworkInitialize (CodeMemberMethod method)
1661 CodeBaseReferenceExpression baseRef = new CodeBaseReferenceExpression ();
1662 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (baseRef, "FrameworkInitialize");
1663 method.Statements.Add (invoke);
1666 void CallSetStringResourcePointer (CodeMemberMethod method)
1668 CodeFieldReferenceExpression stringResource = GetMainClassFieldReferenceExpression ("__stringResource");
1669 method.Statements.Add (
1670 new CodeMethodInvokeExpression (
1671 thisRef,
1672 "SetStringResourcePointer",
1673 new CodeExpression[] {stringResource, new CodePrimitiveExpression (0)})
1677 void CreateFrameworkInitializeMethod ()
1679 CodeMemberMethod method = new CodeMemberMethod ();
1680 method.Name = "FrameworkInitialize";
1681 method.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1682 PrependStatementsToFrameworkInitialize (method);
1683 CallBaseFrameworkInitialize (method);
1684 CallSetStringResourcePointer (method);
1685 AppendStatementsToFrameworkInitialize (method);
1686 mainClass.Members.Add (method);
1689 protected virtual void PrependStatementsToFrameworkInitialize (CodeMemberMethod method)
1693 protected virtual void AppendStatementsToFrameworkInitialize (CodeMemberMethod method)
1695 if (!parser.EnableViewState) {
1696 CodeAssignStatement stmt = new CodeAssignStatement ();
1697 stmt.Left = new CodePropertyReferenceExpression (thisRef, "EnableViewState");
1698 stmt.Right = new CodePrimitiveExpression (false);
1699 method.Statements.Add (stmt);
1702 CodeMethodReferenceExpression methodExpr;
1703 methodExpr = new CodeMethodReferenceExpression (thisRef, "__BuildControlTree");
1704 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (methodExpr, thisRef);
1705 method.Statements.Add (new CodeExpressionStatement (expr));
1708 protected override void AddApplicationAndSessionObjects ()
1710 foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.ApplicationObjects) {
1711 CreateFieldForObject (tag.Type, tag.ObjectID);
1712 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, true, false);
1715 foreach (ObjectTagBuilder tag in GlobalAsaxCompiler.SessionObjects) {
1716 CreateApplicationOrSessionPropertyForObject (tag.Type, tag.ObjectID, false, false);
1720 protected override void CreateStaticFields ()
1722 base.CreateStaticFields ();
1724 CodeMemberField fld = new CodeMemberField (typeof (object), "__stringResource");
1725 fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
1726 fld.InitExpression = new CodePrimitiveExpression (null);
1727 mainClass.Members.Add (fld);
1730 protected void ProcessObjectTag (ObjectTagBuilder tag)
1732 string fieldName = CreateFieldForObject (tag.Type, tag.ObjectID);
1733 CreatePropertyForObject (tag.Type, tag.ObjectID, fieldName, false);
1736 void CreateProperties ()
1738 if (!parser.AutoEventWireup) {
1739 CreateAutoEventWireup ();
1740 } else {
1741 CreateAutoHandlers ();
1744 CreateApplicationInstance ();
1747 void CreateApplicationInstance ()
1749 CodeMemberProperty prop = new CodeMemberProperty ();
1750 Type appType = typeof (HttpApplication);
1751 prop.Type = new CodeTypeReference (appType);
1752 prop.Name = "ApplicationInstance";
1753 prop.Attributes = MemberAttributes.Family | MemberAttributes.Final;
1755 CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression (thisRef, "Context");
1757 propRef = new CodePropertyReferenceExpression (propRef, "ApplicationInstance");
1759 CodeCastExpression cast = new CodeCastExpression (appType.FullName, propRef);
1760 prop.GetStatements.Add (new CodeMethodReturnStatement (cast));
1761 if (partialClass != null)
1762 partialClass.Members.Add (prop);
1763 else
1764 mainClass.Members.Add (prop);
1767 void CreateContentPlaceHolderTemplateProperty (string backingField, string name)
1769 CodeMemberProperty prop = new CodeMemberProperty ();
1770 prop.Type = new CodeTypeReference (typeof (ITemplate));
1771 prop.Name = name;
1772 prop.Attributes = MemberAttributes.Public;
1774 var ret = new CodeMethodReturnStatement ();
1775 var fldRef = new CodeFieldReferenceExpression (thisRef, backingField);
1776 ret.Expression = fldRef;
1777 prop.GetStatements.Add (ret);
1778 prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ()));
1780 prop.CustomAttributes.Add (new CodeAttributeDeclaration ("TemplateContainer", new CodeAttributeArgument [] {
1781 new CodeAttributeArgument (new CodeTypeOfExpression (new CodeTypeReference (typeof (MasterPage))))
1786 var enumValueRef = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeof (TemplateInstance)), "Single");
1787 prop.CustomAttributes.Add (new CodeAttributeDeclaration ("TemplateInstanceAttribute", new CodeAttributeArgument [] {
1788 new CodeAttributeArgument (enumValueRef)
1793 mainClass.Members.Add (prop);
1796 void CreateAutoHandlers ()
1798 // Create AutoHandlers property
1799 CodeMemberProperty prop = new CodeMemberProperty ();
1800 prop.Type = new CodeTypeReference (typeof (int));
1801 prop.Name = "AutoHandlers";
1802 prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1804 CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
1805 CodeFieldReferenceExpression fldRef ;
1806 fldRef = new CodeFieldReferenceExpression (mainClassExpr, "__autoHandlers");
1807 ret.Expression = fldRef;
1808 prop.GetStatements.Add (ret);
1809 prop.SetStatements.Add (new CodeAssignStatement (fldRef, new CodePropertySetValueReferenceExpression ()));
1811 CodeAttributeDeclaration attr = new CodeAttributeDeclaration ("System.Obsolete");
1812 prop.CustomAttributes.Add (attr);
1813 mainClass.Members.Add (prop);
1815 // Add the __autoHandlers field
1816 CodeMemberField fld = new CodeMemberField (typeof (int), "__autoHandlers");
1817 fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
1818 mainClass.Members.Add (fld);
1821 void CreateAutoEventWireup ()
1823 // The getter returns false
1824 CodeMemberProperty prop = new CodeMemberProperty ();
1825 prop.Type = new CodeTypeReference (typeof (bool));
1826 prop.Name = "SupportAutoEvents";
1827 prop.Attributes = MemberAttributes.Family | MemberAttributes.Override;
1828 prop.GetStatements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (false)));
1829 mainClass.Members.Add (prop);
1832 protected virtual string HandleUrlProperty (string str, MemberInfo member)
1834 return str;
1837 TypeConverter GetConverterForMember (MemberInfo member)
1839 TypeDescriptionProvider prov = TypeDescriptor.GetProvider (member.ReflectedType);
1840 if (prov == null)
1841 return null;
1843 ICustomTypeDescriptor desc = prov.GetTypeDescriptor (member.ReflectedType);
1844 PropertyDescriptorCollection coll = desc != null ? desc.GetProperties () : null;
1846 if (coll == null || coll.Count == 0)
1847 return null;
1849 PropertyDescriptor pd = coll.Find (member.Name, false);
1850 if (pd == null)
1851 return null;
1853 return pd.Converter;
1856 CodeExpression CreateNullableExpression (Type type, CodeExpression inst, bool nullable)
1858 if (!nullable)
1859 return inst;
1861 return new CodeObjectCreateExpression (type, new CodeExpression[] {inst});
1864 bool SafeCanConvertFrom (Type type, TypeConverter cvt)
1866 try {
1867 return cvt.CanConvertFrom (type);
1868 } catch (NotImplementedException) {
1869 return false;
1873 bool SafeCanConvertTo (Type type, TypeConverter cvt)
1875 try {
1876 return cvt.CanConvertTo (type);
1877 } catch (NotImplementedException) {
1878 return false;
1882 CodeExpression GetExpressionFromString (Type type, string str, MemberInfo member)
1884 TypeConverter cvt = GetConverterForMember (member);
1885 if (cvt != null && !SafeCanConvertFrom (typeof (string), cvt))
1886 cvt = null;
1888 object convertedFromAttr = null;
1889 bool preConverted = false;
1890 if (cvt != null && str != null) {
1891 convertedFromAttr = cvt.ConvertFromInvariantString (str);
1892 if (convertedFromAttr != null) {
1893 type = convertedFromAttr.GetType ();
1894 preConverted = true;
1898 bool wasNullable = false;
1899 Type originalType = type;
1901 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
1902 Type[] types = type.GetGenericArguments();
1903 originalType = type;
1904 type = types[0]; // we're interested only in the first type here
1905 wasNullable = true;
1908 if (type == typeof (string)) {
1909 object[] urlAttr = member.GetCustomAttributes (typeof (UrlPropertyAttribute), true);
1910 if (urlAttr.Length != 0)
1911 str = HandleUrlProperty ((preConverted && convertedFromAttr is string) ? (string)convertedFromAttr : str, member);
1912 else if (preConverted)
1913 return CreateNullableExpression (originalType,
1914 new CodePrimitiveExpression ((string) convertedFromAttr),
1915 wasNullable);
1917 return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
1918 } else if (type == typeof (bool)) {
1919 if (preConverted)
1920 return CreateNullableExpression (originalType,
1921 new CodePrimitiveExpression ((bool) convertedFromAttr),
1922 wasNullable);
1924 if (str == null || str == "" || InvariantCompareNoCase (str, "true"))
1925 return CreateNullableExpression (originalType, new CodePrimitiveExpression (true), wasNullable);
1926 else if (InvariantCompareNoCase (str, "false"))
1927 return CreateNullableExpression (originalType, new CodePrimitiveExpression (false), wasNullable);
1928 else if (wasNullable && InvariantCompareNoCase(str, "null"))
1929 return new CodePrimitiveExpression (null);
1930 else
1931 throw new ParseException (currentLocation,
1932 "Value '" + str + "' is not a valid boolean.");
1933 } else if (type == monoTypeType)
1934 type = typeof (System.Type);
1936 if (str == null)
1937 return new CodePrimitiveExpression (null);
1939 if (type.IsPrimitive)
1940 return CreateNullableExpression (originalType,
1941 new CodePrimitiveExpression (
1942 Convert.ChangeType (preConverted ? convertedFromAttr : str,
1943 type, Helpers.InvariantCulture)),
1944 wasNullable);
1946 if (type == typeof (string [])) {
1947 string [] subs;
1949 if (preConverted)
1950 subs = (string[])convertedFromAttr;
1951 else
1952 subs = str.Split (',');
1953 CodeArrayCreateExpression expr = new CodeArrayCreateExpression ();
1954 expr.CreateType = new CodeTypeReference (typeof (string));
1955 foreach (string v in subs)
1956 expr.Initializers.Add (new CodePrimitiveExpression (v.Trim ()));
1958 return CreateNullableExpression (originalType, expr, wasNullable);
1961 if (type == typeof (Color)) {
1962 Color c;
1964 if (!preConverted) {
1965 if (colorConverter == null)
1966 colorConverter = TypeDescriptor.GetConverter (typeof (Color));
1968 if (str.Trim().Length == 0) {
1969 CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (typeof (Color));
1970 return CreateNullableExpression (originalType,
1971 new CodeFieldReferenceExpression (ft, "Empty"),
1972 wasNullable);
1975 try {
1976 if (str.IndexOf (',') == -1) {
1977 c = (Color) colorConverter.ConvertFromString (str);
1978 } else {
1979 int [] argb = new int [4];
1980 argb [0] = 255;
1982 string [] parts = str.Split (',');
1983 int length = parts.Length;
1984 if (length < 3)
1985 throw new Exception ();
1987 int basei = (length == 4) ? 0 : 1;
1988 for (int i = length - 1; i >= 0; i--) {
1989 argb [basei + i] = (int) Byte.Parse (parts [i]);
1991 c = Color.FromArgb (argb [0], argb [1], argb [2], argb [3]);
1993 } catch (Exception e) {
1994 // Hack: "LightGrey" is accepted, but only for ASP.NET, as the
1995 // TypeConverter for Color fails to ConvertFromString.
1996 // Hence this hack...
1997 if (InvariantCompareNoCase ("LightGrey", str)) {
1998 c = Color.LightGray;
1999 } else {
2000 throw new ParseException (currentLocation,
2001 "Color " + str + " is not a valid color.", e);
2004 } else
2005 c = (Color)convertedFromAttr;
2007 if (c.IsKnownColor) {
2008 CodeFieldReferenceExpression expr = new CodeFieldReferenceExpression ();
2009 if (c.IsSystemColor)
2010 type = typeof (SystemColors);
2012 expr.TargetObject = new CodeTypeReferenceExpression (type);
2013 expr.FieldName = c.Name;
2014 return CreateNullableExpression (originalType, expr, wasNullable);
2015 } else {
2016 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
2017 m.TargetObject = new CodeTypeReferenceExpression (type);
2018 m.MethodName = "FromArgb";
2019 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
2020 invoke.Parameters.Add (new CodePrimitiveExpression (c.A));
2021 invoke.Parameters.Add (new CodePrimitiveExpression (c.R));
2022 invoke.Parameters.Add (new CodePrimitiveExpression (c.G));
2023 invoke.Parameters.Add (new CodePrimitiveExpression (c.B));
2024 return CreateNullableExpression (originalType, invoke, wasNullable);
2028 TypeConverter converter = preConverted ? cvt : wasNullable ? TypeDescriptor.GetConverter (type) : null;
2029 if (converter == null) {
2030 PropertyDescriptor pdesc = TypeDescriptor.GetProperties (member.DeclaringType) [member.Name];
2031 if (pdesc != null)
2032 converter = pdesc.Converter;
2033 else {
2034 Type memberType;
2035 switch (member.MemberType) {
2036 case MemberTypes.Field:
2037 memberType = ((FieldInfo)member).FieldType;
2038 break;
2040 case MemberTypes.Property:
2041 memberType = ((PropertyInfo)member).PropertyType;
2042 break;
2044 default:
2045 memberType = null;
2046 break;
2049 if (memberType == null)
2050 return null;
2052 converter = TypeDescriptor.GetConverter (memberType);
2056 if (preConverted || (converter != null && SafeCanConvertFrom (typeof (string), converter))) {
2057 object value = preConverted ? convertedFromAttr : converter.ConvertFromInvariantString (str);
2059 if (SafeCanConvertTo (typeof (InstanceDescriptor), converter)) {
2060 InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor));
2061 if (wasNullable)
2062 return CreateNullableExpression (originalType, GenerateInstance (idesc, true),
2063 wasNullable);
2065 CodeExpression instance = GenerateInstance (idesc, true);
2066 if (type.IsPublic)
2067 return new CodeCastExpression (type, instance);
2068 else
2069 return instance;
2072 CodeExpression exp = GenerateObjectInstance (value, false);
2073 if (exp != null)
2074 return CreateNullableExpression (originalType, exp, wasNullable);
2076 CodeMethodReferenceExpression m = new CodeMethodReferenceExpression ();
2077 m.TargetObject = new CodeTypeReferenceExpression (typeof (TypeDescriptor));
2078 m.MethodName = "GetConverter";
2079 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (m);
2080 CodeTypeReference tref = new CodeTypeReference (type);
2081 invoke.Parameters.Add (new CodeTypeOfExpression (tref));
2083 invoke = new CodeMethodInvokeExpression (invoke, "ConvertFrom");
2084 invoke.Parameters.Add (new CodePrimitiveExpression (str));
2086 if (wasNullable)
2087 return CreateNullableExpression (originalType, invoke, wasNullable);
2089 return new CodeCastExpression (type, invoke);
2092 Console.WriteLine ("Unknown type: " + type + " value: " + str);
2094 return CreateNullableExpression (originalType, new CodePrimitiveExpression (str), wasNullable);
2097 CodeExpression GenerateInstance (InstanceDescriptor idesc, bool throwOnError)
2099 CodeExpression[] parameters = new CodeExpression [idesc.Arguments.Count];
2100 int n = 0;
2101 foreach (object ob in idesc.Arguments) {
2102 CodeExpression exp = GenerateObjectInstance (ob, throwOnError);
2103 if (exp == null) return null;
2104 parameters [n++] = exp;
2107 switch (idesc.MemberInfo.MemberType) {
2108 case MemberTypes.Constructor:
2109 CodeTypeReference tob = new CodeTypeReference (idesc.MemberInfo.DeclaringType);
2110 return new CodeObjectCreateExpression (tob, parameters);
2112 case MemberTypes.Method:
2113 CodeTypeReferenceExpression mt = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
2114 return new CodeMethodInvokeExpression (mt, idesc.MemberInfo.Name, parameters);
2116 case MemberTypes.Field:
2117 CodeTypeReferenceExpression ft = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
2118 return new CodeFieldReferenceExpression (ft, idesc.MemberInfo.Name);
2120 case MemberTypes.Property:
2121 CodeTypeReferenceExpression pt = new CodeTypeReferenceExpression (idesc.MemberInfo.DeclaringType);
2122 return new CodePropertyReferenceExpression (pt, idesc.MemberInfo.Name);
2124 throw new ParseException (currentLocation, "Invalid instance type.");
2127 CodeExpression GenerateObjectInstance (object value, bool throwOnError)
2129 if (value == null)
2130 return new CodePrimitiveExpression (null);
2132 if (value is System.Type) {
2133 CodeTypeReference tref = new CodeTypeReference (value.ToString ());
2134 return new CodeTypeOfExpression (tref);
2137 Type t = value.GetType ();
2139 if (t.IsPrimitive || value is string)
2140 return new CodePrimitiveExpression (value);
2142 if (t.IsArray) {
2143 Array ar = (Array) value;
2144 CodeExpression[] items = new CodeExpression [ar.Length];
2145 for (int n=0; n<ar.Length; n++) {
2146 CodeExpression exp = GenerateObjectInstance (ar.GetValue (n), throwOnError);
2147 if (exp == null) return null;
2148 items [n] = exp;
2150 return new CodeArrayCreateExpression (new CodeTypeReference (t), items);
2153 TypeConverter converter = TypeDescriptor.GetConverter (t);
2154 if (converter != null && converter.CanConvertTo (typeof (InstanceDescriptor))) {
2155 InstanceDescriptor idesc = (InstanceDescriptor) converter.ConvertTo (value, typeof(InstanceDescriptor));
2156 return GenerateInstance (idesc, throwOnError);
2159 InstanceDescriptor desc = GetDefaultInstanceDescriptor (value);
2160 if (desc != null) return GenerateInstance (desc, throwOnError);
2162 if (throwOnError)
2163 throw new ParseException (currentLocation, "Cannot generate an instance for the type: " + t);
2164 else
2165 return null;
2168 InstanceDescriptor GetDefaultInstanceDescriptor (object value)
2170 if (value is System.Web.UI.WebControls.Unit) {
2171 System.Web.UI.WebControls.Unit s = (System.Web.UI.WebControls.Unit) value;
2172 if (s.IsEmpty) {
2173 FieldInfo f = typeof (Unit).GetField ("Empty");
2174 return new InstanceDescriptor (f, null);
2176 ConstructorInfo c = typeof(System.Web.UI.WebControls.Unit).GetConstructor (
2177 BindingFlags.Instance | BindingFlags.Public,
2178 null,
2179 new Type[] {typeof(double), typeof(System.Web.UI.WebControls.UnitType)},
2180 null);
2182 return new InstanceDescriptor (c, new object[] {s.Value, s.Type});
2185 if (value is System.Web.UI.WebControls.FontUnit) {
2186 System.Web.UI.WebControls.FontUnit s = (System.Web.UI.WebControls.FontUnit) value;
2187 if (s.IsEmpty) {
2188 FieldInfo f = typeof (FontUnit).GetField ("Empty");
2189 return new InstanceDescriptor (f, null);
2192 Type cParamType = null;
2193 object cParam = null;
2195 switch (s.Type) {
2196 case FontSize.AsUnit:
2197 case FontSize.NotSet:
2198 cParamType = typeof (System.Web.UI.WebControls.Unit);
2199 cParam = s.Unit;
2200 break;
2202 default:
2203 cParamType = typeof (string);
2204 cParam = s.Type.ToString ();
2205 break;
2208 ConstructorInfo c = typeof(System.Web.UI.WebControls.FontUnit).GetConstructor (
2209 BindingFlags.Instance | BindingFlags.Public,
2210 null,
2211 new Type[] {cParamType},
2212 null);
2213 if (c != null)
2214 return new InstanceDescriptor (c, new object[] {cParam});
2216 return null;
2219 #if DEBUG
2220 CodeMethodInvokeExpression CreateConsoleWriteLineCall (string format, params CodeExpression[] parms)
2222 CodeMethodReferenceExpression cwl = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (System.Console)), "WriteLine");
2223 CodeMethodInvokeExpression cwlCall = new CodeMethodInvokeExpression (cwl);
2225 cwlCall.Parameters.Add (new CodePrimitiveExpression (format));
2226 if (parms != null && parms.Length > 0)
2227 foreach (CodeExpression expr in parms)
2228 cwlCall.Parameters.Add (expr);
2230 return cwlCall;
2232 #endif