2 // System.Web.Compilation.AspGenerator
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
10 using System.Collections;
11 using System.ComponentModel;
13 using System.Diagnostics;
15 using System.Reflection;
17 using System.Text.RegularExpressions;
19 using System.Web.UI.HtmlControls;
20 using System.Web.UI.WebControls;
21 using System.Web.Util;
23 namespace System.Web.Compilation
28 private Stack controls;
29 private ControlStackData top;
30 private bool space_between_tags;
31 private bool sbt_valid;
33 class ControlStackData
35 public Type controlType;
36 public string controlID;
38 public ChildrenKind childKind;
39 public string defaultPropertyName;
40 public int childrenNumber;
41 public Type container;
42 public StringBuilder dataBindFunction;
43 public StringBuilder codeRenderFunction;
44 public bool useCodeRender;
45 public int codeRenderIndex;
47 public ControlStackData (Type controlType,
50 ChildrenKind childKind,
51 string defaultPropertyName,
54 this.controlType = controlType;
55 this.controlID = controlID;
57 this.childKind = childKind;
58 this.defaultPropertyName = defaultPropertyName;
59 this.container = container;
63 public override string ToString ()
65 return controlType + " " + controlID + " " + tagID + " " + childKind + " " + childrenNumber;
69 public ControlStack ()
71 controls = new Stack ();
74 private Type GetContainerType (Type type)
76 if (type != typeof (System.Web.UI.Control) &&
77 !type.IsSubclassOf (typeof (System.Web.UI.Control)))
81 if (type == typeof (System.Web.UI.WebControls.DataList))
82 container_type = typeof (System.Web.UI.WebControls.DataListItem);
83 else if (type == typeof (System.Web.UI.WebControls.DataGrid))
84 container_type = typeof (System.Web.UI.WebControls.DataGridItem);
85 else if (type == typeof (System.Web.UI.WebControls.Repeater))
86 container_type = typeof (System.Web.UI.WebControls.RepeaterItem);
87 else if (type == typeof (ListControl) || type.IsSubclassOf (typeof (ListControl)))
88 container_type = type;
90 container_type = Container;
92 return container_type;
95 public void Push (object o)
97 if (!(o is ControlStackData))
101 top = (ControlStackData) o;
105 public void Push (Type controlType,
108 ChildrenKind childKind,
109 string defaultPropertyName)
111 Type container_type = null;
112 if (controlType != null){
114 container_type = GetContainerType (controlType);
115 if (container_type == null)
116 container_type = this.Container;
119 top = new ControlStackData (controlType,
131 object item = controls.Pop ();
132 if (controls.Count != 0)
133 top = (ControlStackData) controls.Peek ();
138 public Type PeekType ()
140 return top.controlType;
143 public string PeekControlID ()
145 return top.controlID;
148 public string PeekTagID ()
153 public ChildrenKind PeekChildKind ()
155 return top.childKind;
158 public string PeekDefaultPropertyName ()
160 return top.defaultPropertyName;
163 public void AddChild ()
166 top.childrenNumber++;
169 public bool HasDataBindFunction ()
171 if (top.dataBindFunction == null || top.dataBindFunction.Length == 0)
176 public bool UseCodeRender
179 if (top.codeRenderFunction == null || top.codeRenderFunction.Length == 0)
181 return top.useCodeRender;
184 set { top.useCodeRender= value; }
187 public bool SpaceBetweenTags
192 Type type = top.controlType;
193 if (type.Namespace == "System.Web.UI.WebControls")
194 space_between_tags = true;
195 else if (type.IsSubclassOf (typeof (System.Web.UI.WebControls.WebControl)))
196 space_between_tags = true;
197 else if (type == typeof (System.Web.UI.HtmlControls.HtmlSelect))
198 space_between_tags = true;
199 else if (type == typeof (System.Web.UI.HtmlControls.HtmlTable))
200 space_between_tags = true;
201 else if (type == typeof (System.Web.UI.HtmlControls.HtmlTableRow))
202 space_between_tags = true;
203 else if (type == typeof (System.Web.UI.HtmlControls.HtmlTableCell))
204 space_between_tags = true;
206 space_between_tags = false;
208 return space_between_tags;
212 public Type Container {
217 return top.container;
221 public StringBuilder DataBindFunction
224 if (top.dataBindFunction == null)
225 top.dataBindFunction = new StringBuilder ();
226 return top.dataBindFunction;
230 public int CodeRenderIndex {
232 return top.codeRenderIndex++;
236 public StringBuilder CodeRenderFunction
239 if (top.codeRenderFunction == null)
240 top.codeRenderFunction = new StringBuilder ();
241 return top.codeRenderFunction;
245 public int ChildIndex
247 get { return top.childrenNumber - 1; }
252 get { return controls.Count; }
255 public override string ToString ()
257 return top.ToString () + " " + top.useCodeRender;
272 StringBuilder prolog;
273 StringBuilder declarations;
274 StringBuilder script;
275 StringBuilder constructor;
276 StringBuilder init_funcs;
277 StringBuilder epilog;
278 StringBuilder current_function;
279 StringBuilder textChunk;
281 ControlStack controls;
284 AspComponentFoundry aspFoundry;
295 string privateBinPath;
296 string main_directive;
297 static string app_file_wrong = "The content in the application file is not valid.";
304 ScriptStatus sstatus = ScriptStatus.Close;
306 ArrayList dependencies;
310 SessionState sessionState = SessionState.Enabled;
312 static Type styleType = typeof (System.Web.UI.WebControls.Style);
313 static Type fontinfoType = typeof (System.Web.UI.WebControls.FontInfo);
315 enum UserControlResult
319 CompilationFailed = 2
329 public AspGenerator (string pathToFile)
331 if (pathToFile == null)
332 throw new ArgumentNullException ("pathToFile");
334 string filename = Path.GetFileName (pathToFile);
335 this.className = filename.Replace ('.', '_'); // Overridden by @ Page classname
336 this.className = className.Replace ('-', '_');
337 this.className = className.Replace (' ', '_');
338 Options ["ClassName"] = this.className;
339 this.fullPath = Path.GetFullPath (pathToFile);
341 this.has_form_tag = false;
342 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
343 privateBinPath = setup.PrivateBinPath;
344 // This is a hack until we can run stuff in different domains
345 if (privateBinPath == null || privateBinPath.Length == 0)
346 privateBinPath = "bin";
348 if (!Path.IsPathRooted (privateBinPath)) {
349 string appbase = setup.ApplicationBase;
350 if (appbase.StartsWith ("file://")) {
351 appbase = appbase.Substring (7);
352 if (Path.DirectorySeparatorChar != '/')
353 appbase = appbase.Replace ('/', Path.DirectorySeparatorChar);
355 privateBinPath = Path.Combine (appbase, privateBinPath);
361 public string BaseType {
362 get { return basetype; }
369 isUserControl = (basetype == "System.Web.UI.UserControl");
370 isPage = (basetype == "System.Web.UI.Page");
371 isApplication = (basetype == "System.Web.HttpApplication");
375 public bool IsUserControl {
376 get { return isUserControl; }
380 get { return isPage; }
383 public bool IsApplication {
384 get { return isApplication; }
387 public string Interfaces {
388 get { return interfaces; }
391 public Hashtable Options {
394 options = new Hashtable ();
400 public ArrayList Dependencies {
401 get { return dependencies; }
404 internal HttpContext Context {
405 get { return context; }
406 set { context = value; }
409 bool AddUsing (string nspace)
411 string _using = "using " + nspace + ";";
412 if (prolog.ToString ().IndexOf (_using) == -1) {
413 prolog.AppendFormat ("\t{0}\n", _using);
420 void AddInterface (Type type)
422 AddInterface (type.ToString ());
425 public void AddInterface (string iface)
427 if (interfaces == null) {
428 interfaces = ", " + iface;
430 string s = ", " + iface;
431 if (interfaces.IndexOf (s) == -1)
436 private AspComponentFoundry Foundry
439 if (aspFoundry == null)
440 aspFoundry = new AspComponentFoundry ();
448 dependencies = new ArrayList ();
449 dependencies.Add (fullPath);
451 controls = new ControlStack ();
452 controls.Push (typeof (System.Web.UI.Control), "Root", null, ChildrenKind.CONTROLS, null);
453 prolog = new StringBuilder ();
454 declarations = new StringBuilder ();
455 script = new StringBuilder ();
456 constructor = new StringBuilder ();
457 init_funcs = new StringBuilder ();
458 epilog = new StringBuilder ();
459 textChunk = new StringBuilder ();
461 current_function = new StringBuilder ();
462 functions = new Stack ();
463 functions.Push (current_function);
465 parts = new Object [6];
467 parts [1] = declarations;
469 parts [3] = constructor;
470 parts [4] = init_funcs;
473 prolog.Append ("namespace ASP {\n" +
474 "\tusing System;\n" +
475 "\tusing System.Collections;\n" +
476 "\tusing System.Collections.Specialized;\n" +
477 "\tusing System.Configuration;\n" +
478 "\tusing System.IO;\n" +
479 "\tusing System.Text;\n" +
480 "\tusing System.Text.RegularExpressions;\n" +
481 "\tusing System.Web;\n" +
482 "\tusing System.Web.Caching;\n" +
483 "\tusing System.Web.Security;\n" +
484 "\tusing System.Web.SessionState;\n" +
485 "\tusing System.Web.UI;\n" +
486 "\tusing System.Web.UI.WebControls;\n" +
487 "\tusing System.Web.UI.HtmlControls;\n");
489 declarations.Append ("\t\tprivate static int __autoHandlers;\n");
491 current_function.Append ("\t\tprivate void __BuildControlTree (System.Web.UI.Control __ctrl)\n\t\t{\n");
493 current_function.Append ("\t\t\tSystem.Web.UI.IParserAccessor __parser = " +
494 "(System.Web.UI.IParserAccessor) __ctrl;\n\n");
496 controls.UseCodeRender = true;
499 public TextReader GetCode ()
502 throw new InvalidOperationException ("Parsing not done yet! (may be there were errors?)");
504 StringBuilder code = new StringBuilder ();
505 for (int i = 0; i < parts.Length; i++)
506 code.Append ((StringBuilder) parts [i]);
508 return new StringReader (code.ToString ());
511 // Regex.Escape () make some illegal escape sequences for a C# source.
512 private string Escape (string input)
517 string output = input.Replace ("\\", "\\\\");
518 output = output.Replace ("\"", "\\\"");
519 output = output.Replace ("\t", "\\t");
520 output = output.Replace ("\r", "\\r");
521 output = output.Replace ("\n", "\\n");
522 output = output.Replace ("\n", "\\n");
526 bool AddProtectedField (Type type, string fieldName)
528 if (parentType == null) {
529 declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
533 FieldInfo field = parentType.GetField (fieldName, BindingFlags.Public |
534 BindingFlags.NonPublic |
535 BindingFlags.Instance |
536 BindingFlags.Static);
538 if (field == null || (!field.IsPublic && !field.IsFamily)) {
539 declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
543 if (!field.FieldType.IsAssignableFrom (type)) {
544 string message = String.Format ("The base class includes the field '{0}', but its " +
545 "type '{1}' is not compatible with {2}",
546 fieldName, field.FieldType, type);
548 throw new ApplicationException (message);
554 private Type LoadParentType (string typeName)
556 // First try loaded assemblies, then try assemblies in Bin directory.
557 // By now i do this 'by hand' but may be this is a runtime/gac task.
559 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
560 foreach (Assembly ass in assemblies) {
561 type = ass.GetType (typeName);
567 string [] binDlls = Directory.GetFiles (privateBinPath, "*.dll");
568 foreach (string dll in binDlls) {
569 string dllPath = Path.Combine (privateBinPath, dll);
572 assembly = Assembly.LoadFrom (dllPath);
573 type = assembly.GetType (typeName);
574 } catch (Exception e) {
575 if (assembly != null) {
576 Console.WriteLine ("ASP.NET Warning: assembly {0} loaded", dllPath);
577 Console.WriteLine ("ASP.NET Warning: but type {0} not found", typeName);
579 Console.WriteLine ("ASP.NET Warning: unable to load type {0} from {1}",
582 Console.WriteLine ("ASP.NET Warning: error was: {0}", e.Message);
586 dependencies.Add (dllPath);
594 private void PageDirective (TagAttributes att)
596 if (att ["ClassName"] != null){
597 this.className = (string) att ["ClassName"];
598 Options ["ClassName"] = className;
601 if (att ["EnableSessionState"] != null){
603 throw new ApplicationException ("EnableSessionState not allowed here.");
605 string est = (string) att ["EnableSessionState"];
606 if (0 == String.Compare (est, "false", true))
607 sessionState = SessionState.Disabled;
608 else if (0 == String.Compare (est, "true", true))
609 sessionState = SessionState.Enabled;
610 else if (0 == String.Compare (est, "readonly", true))
611 sessionState = SessionState.ReadOnly;
613 throw new ApplicationException ("EnableSessionState in Page directive not set to " +
614 "a correct value: " + est);
617 if (att ["Inherits"] != null) {
618 parent = (string) att ["Inherits"];
619 parentType = LoadParentType (parent);
620 if (parentType == null)
621 throw new ApplicationException ("The class " + parent + " cannot be found.");
624 if (att ["CompilerOptions"] != null)
625 Options ["CompilerOptions"] = (string) att ["CompilerOptions"];
627 if (att ["AutoEventWireup"] != null) {
628 if (options ["AutoEventWireup"] != null)
629 throw new ApplicationException ("Already have an AutoEventWireup attribute");
631 bool autoevent = true;
632 string v = att ["AutoEventWireup"] as string;
634 autoevent = Convert.ToBoolean (v);
635 } catch (Exception) {
636 throw new ApplicationException ("'" + v + "' is not a valid value for AutoEventWireup");
638 options ["AutoEventWireup"] = autoevent;
641 //FIXME: add support for more attributes.
644 void AddReference (string dll)
646 string references = Options ["References"] as string;
647 if (references == null)
650 references = references + "|" + dll;
652 Options ["References"] = references;
655 private void RegisterDirective (TagAttributes att)
657 string tag_prefix = (string) (att ["tagprefix"] == null ? "" : att ["tagprefix"]);
658 string name_space = (string) (att ["namespace"] == null ? "" : att ["namespace"]);
659 string assembly_name = (string) (att ["assembly"] == null ? "" : att ["assembly"]);
660 string tag_name = (string) (att ["tagname"] == null ? "" : att ["tagname"]);
661 string src = (string) (att ["src"] == null ? "" : att ["src"]);
663 if (tag_prefix != "" && name_space != "" && assembly_name != ""){
664 if (tag_name != "" || src != "")
665 throw new ApplicationException ("Invalid attributes for @ Register: " +
668 AddUsing (name_space);
669 string dll = Path.Combine (privateBinPath, assembly_name + ".dll");
670 // Hack: it should use assembly.load semantics...
671 // may be when we don't run mcs as a external program...
672 if (!File.Exists (dll))
675 dependencies.Add (dll);
677 Foundry.RegisterFoundry (tag_prefix, dll, name_space);
682 if (tag_prefix != "" && tag_name != "" && src != ""){
683 if (name_space != "" && assembly_name != "")
684 throw new ApplicationException ("Invalid attributes for @ Register: " +
687 if (!src.EndsWith (".ascx"))
688 throw new ApplicationException ("Source file extension for controls " +
691 UserControlData data = GenerateUserControl (src, Context);
692 switch (data.result) {
693 case UserControlResult.OK:
695 Foundry.RegisterFoundry (tag_prefix, tag_name, data.assemblyName, "ASP", data.className);
696 AddReference (data.assemblyName);
698 case UserControlResult.FileNotFound:
699 throw new ApplicationException ("File '" + src + "' not found.");
700 case UserControlResult.CompilationFailed:
701 //TODO: should say where the generated .cs file is for the server to
702 //show the source and the compiler error
703 throw new NotImplementedException ();
708 throw new ApplicationException ("Invalid combination of attributes in " +
709 "@ Register: " + att.ToString ());
712 private void ProcessDirective ()
715 Directive directive = (Directive) current;
716 TagAttributes att = directive.Attributes;
721 string id = directive.TagID.ToUpper ();
724 if (main_directive != null)
725 throw new ApplicationException (id + " not allowed after " + main_directive);
728 throw new ApplicationException ("@Application not allowed.");
730 string inherits = att ["inherits"] as string;
731 if (inherits != null) {
733 parentType = LoadParentType (parent);
734 if (parentType == null)
735 throw new ApplicationException ("The class " + parent + " cannot be found.");
738 main_directive = directive.TagID;
742 if (main_directive != null)
743 throw new ApplicationException (id + " not allowed after " + main_directive);
745 if (IsUserControl && id != "CONTROL")
746 throw new ApplicationException ("@Page not allowed for user controls.");
747 else if (IsPage && id != "PAGE")
748 throw new ApplicationException ("@Control not allowed here. This is a page!");
751 main_directive = directive.TagID;
754 value = att ["namespace"] as string;
755 if (value == null || att.Count > 1)
756 throw new ApplicationException ("Wrong syntax in Import directive.");
758 string _using = "using " + value + ";";
759 if (AddUsing (value) == true) {
760 string imports = Options ["Import"] as string;
761 if (imports == null) {
764 imports += "," + value;
767 Options ["Import"] = imports;
772 throw new ApplicationException ("@ Implements not allowed in an application file.");
774 string iface = (string) att ["interface"];
775 AddInterface (iface);
779 throw new ApplicationException ("@ Register not allowed in an application file.");
781 RegisterDirective (att);
785 throw new ApplicationException ("Wrong syntax in Assembly directive.");
787 string name = att ["name"] as string;
788 string src = att ["src"] as string;
790 if (name == null && src == null)
791 throw new ApplicationException ("Wrong syntax in Assembly directive.");
793 if (IsApplication && src != null)
794 throw new ApplicationException ("'name' attribute expected.");
796 value = (name == null) ? src : name;
797 string assemblies = Options ["Assembly"] as string;
798 if (assemblies == null) {
801 assemblies += "," + value;
804 Options ["Assembly"] = assemblies;
809 private void ProcessPlainText ()
811 PlainText asis = (PlainText) current;
812 string trimmed = asis.Text.Trim ();
813 if (trimmed == String.Empty && controls.SpaceBetweenTags == true)
817 if (trimmed != String.Empty)
818 throw new ApplicationException (app_file_wrong);
822 if (trimmed != String.Empty && controls.PeekChildKind () != ChildrenKind.CONTROLS){
823 string tag_id = controls.PeekTagID ();
824 throw new ApplicationException ("Literal content not allowed for " + tag_id);
827 string escaped_text = Escape (asis.Text);
828 current_function.AppendFormat ("\t\t\t__parser.AddParsedSubObject (" +
829 "new System.Web.UI.LiteralControl (\"{0}\"));\n",
831 StringBuilder codeRenderFunction = controls.CodeRenderFunction;
832 codeRenderFunction.AppendFormat ("\t\t\t__output.Write (\"{0}\");\n", escaped_text);
835 private string EnumValueNameToString (Type enum_type, string value_name)
837 if (value_name.EndsWith ("*"))
838 throw new ApplicationException ("Invalid property value: '" + value_name +
839 ". It must be a valid " + enum_type.ToString () + " value.");
841 MemberInfo [] nested_types = enum_type.FindMembers (MemberTypes.Field,
842 BindingFlags.Public | BindingFlags.Static,
843 Type.FilterNameIgnoreCase,
846 if (nested_types.Length == 0)
847 throw new ApplicationException ("Value " + value_name + " not found in enumeration " +
848 enum_type.ToString ());
849 if (nested_types.Length > 1)
850 throw new ApplicationException ("Value " + value_name + " found " +
851 nested_types.Length + " in enumeration " +
852 enum_type.ToString ());
854 return enum_type.ToString () + "." + nested_types [0].Name;
857 private void NewControlFunction (string tag_id,
860 ChildrenKind children_kind,
861 string defaultPropertyName)
863 ChildrenKind prev_children_kind = controls.PeekChildKind ();
864 if (prev_children_kind == ChildrenKind.NONE ||
865 prev_children_kind == ChildrenKind.PROPERTIES){
866 string prev_tag_id = controls.PeekTagID ();
867 throw new ApplicationException ("Child controls not allowed for " + prev_tag_id);
871 if (prev_children_kind == ChildrenKind.DBCOLUMNS &&
872 !typeof (DataGridColumn).IsAssignableFrom (control_type)) {
873 allowed = typeof (DataGridColumn);
874 } else if (prev_children_kind == ChildrenKind.LISTITEM &&
875 control_type != typeof (System.Web.UI.WebControls.ListItem)) {
876 allowed = typeof (DataGridColumn);
877 } else if (prev_children_kind == ChildrenKind.HTMLROW) {
878 Type prevType = controls.PeekType ();
879 if (prevType == typeof (HtmlTable) && control_type != typeof (HtmlTableRow))
880 allowed = typeof (HtmlTableRow);
881 else if (prevType == typeof (Table) && control_type != typeof (TableRow))
882 allowed = typeof (TableRow);
883 } else if (prev_children_kind == ChildrenKind.HTMLCELL) {
884 Type prevType = controls.PeekType ();
885 if (prevType == typeof (HtmlTableRow) && control_type != typeof (HtmlTableCell))
886 allowed = typeof (HtmlTableCell);
887 else if (prevType == typeof (TableRow) && control_type != typeof (TableCell))
888 allowed = typeof (TableCell);
892 throw new ApplicationException ("Inside " + controls.PeekTagID () + " only " +
893 allowed + " objects are allowed");
895 StringBuilder func_code = new StringBuilder ();
896 current_function = func_code;
897 if (0 == String.Compare (tag_id, "form", true)){
899 throw new ApplicationException ("Only one form server tag allowed.");
903 controls.Push (control_type, control_id, tag_id, children_kind, defaultPropertyName);
904 bool is_generic = control_type == typeof (System.Web.UI.HtmlControls.HtmlGenericControl);
905 functions.Push (current_function);
906 if (control_type != typeof (System.Web.UI.WebControls.ListItem) &&
907 prev_children_kind != ChildrenKind.DBCOLUMNS) {
908 current_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_" +
909 "{0} ()\n\t\t{{\n\t\t\t{1} __ctrl;\n\n\t\t\t__ctrl" +
910 " = new {1} ({2});\n\t\t\tthis.{0} = __ctrl;\n",
911 control_id, control_type,
912 (is_generic? "\"" + tag_id + "\"" : ""));
914 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ()\n\t\t{{" +
915 "\n\t\t\t{1} __ctrl;\n\t\t\t__ctrl = new {1} ();" +
916 "\n\t\t\tthis.{0} = __ctrl;\n",
917 control_id, control_type);
920 if (children_kind == ChildrenKind.CONTROLS || children_kind == ChildrenKind.OPTION)
921 current_function.Append ("\t\t\tSystem.Web.UI.IParserAccessor __parser = " +
922 "(System.Web.UI.IParserAccessor) __ctrl;\n");
925 private void DataBoundProperty (Type target, string varName, string value)
928 throw new ApplicationException ("Empty data binding tag.");
930 string control_id = controls.PeekControlID ();
931 string control_type_string = controls.PeekType ().ToString ();
932 StringBuilder db_function = controls.DataBindFunction;
934 if (controls.Container == null || !typeof (INamingContainer).IsAssignableFrom (controls.Container))
935 container = "System.Web.UI.Control";
937 container = controls.Container.ToString ();
940 if (db_function.Length == 0)
941 db_function.AppendFormat ("\t\tpublic void __DataBind_{0} (object sender, " +
942 "System.EventArgs e) {{\n" +
943 "\t\t\t{1} Container;\n" +
944 "\t\t\t{2} target;\n" +
945 "\t\t\ttarget = ({2}) sender;\n" +
946 "\t\t\tContainer = ({1}) target.BindingContainer;\n",
947 control_id, container, control_type_string);
949 /* Removes '<%#' and '%>' */
950 string real_value = value.Remove (0,3);
951 real_value = real_value.Remove (real_value.Length - 2, 2);
952 real_value = real_value.Trim ();
954 if (target == typeof (string))
955 db_function.AppendFormat ("\t\t\ttarget.{0} = System.Convert.ToString ({1});\n",
956 varName, real_value);
958 db_function.AppendFormat ("\t\t\ttarget.{0} = ({1}) ({2});\n",
959 varName, target, real_value);
963 * Returns true if it generates some code for the specified property
965 private void AddCodeForPropertyOrField (Type type, string var_name, string att, bool isDataBound)
967 /* FIXME: should i check for this or let the compiler fail?
968 * if (!prop.CanWrite)
972 DataBoundProperty (type, var_name, att);
974 else if (type == typeof (string)){
976 throw new ApplicationException ("null value for attribute " + var_name );
978 current_function.AppendFormat ("\t\t\t__ctrl.{0} = \"{1}\";\n", var_name,
979 Escape (att)); // FIXME: really Escape this?
981 else if (type.IsEnum){
983 throw new ApplicationException ("null value for attribute " + var_name );
985 string enum_value = EnumValueNameToString (type, att);
987 current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, enum_value);
989 else if (type == typeof (bool)){
992 value = "true"; //FIXME: is this ok for non Style properties?
993 else if (0 == String.Compare (att, "true", true))
995 else if (0 == String.Compare (att, "false", true))
998 throw new ApplicationException ("Value '" + att + "' is not a valid boolean.");
1000 current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1002 else if (type == typeof (System.Web.UI.WebControls.Unit)){
1003 //FIXME: should use the culture specified in Page
1005 Unit value = Unit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
1006 } catch (Exception) {
1007 throw new ApplicationException ("'" + att + "' cannot be parsed as a unit.");
1009 current_function.AppendFormat ("\t\t\t__ctrl.{0} = " +
1010 "System.Web.UI.WebControls.Unit.Parse (\"{1}\", " +
1011 "System.Globalization.CultureInfo.InvariantCulture);\n",
1014 else if (type == typeof (System.Web.UI.WebControls.FontUnit)){
1015 //FIXME: should use the culture specified in Page
1017 FontUnit value = FontUnit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
1018 } catch (Exception) {
1019 throw new ApplicationException ("'" + att + "' cannot be parsed as a unit.");
1021 current_function.AppendFormat ("\t\t\t__ctrl.{0} = " +
1022 "System.Web.UI.WebControls.FontUnit.Parse (\"{1}\", " +
1023 "System.Globalization.CultureInfo.InvariantCulture);\n",
1026 else if (type == typeof (Int16) || type == typeof (Int32) || type == typeof (Int64)) {
1029 value = Int64.Parse (att); //FIXME: should use the culture specified in Page
1030 } catch (Exception){
1031 throw new ApplicationException (att + " is not a valid signed number " +
1032 "or is out of range.");
1035 current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1037 else if (type == typeof (UInt16) || type == typeof (UInt32) || type == typeof (UInt64)) {
1040 value = UInt64.Parse (att); //FIXME: should use the culture specified in Page
1041 } catch (Exception){
1042 throw new ApplicationException (att + " is not a valid unsigned number " +
1043 "or is out of range.");
1046 current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1048 else if (type == typeof (float)) {
1051 value = Single.Parse (att);
1052 } catch (Exception){
1053 throw new ApplicationException (att + " is not avalid float number or " +
1054 "is out of range.");
1057 current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1059 else if (type == typeof (double)){
1062 value = Double.Parse (att);
1063 } catch (Exception){
1064 throw new ApplicationException (att + " is not avalid double number or " +
1065 "is out of range.");
1068 current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
1070 else if (type == typeof (System.Drawing.Color)){
1073 c = (Color) TypeDescriptor.GetConverter (typeof (Color)).ConvertFromString (att);
1074 } catch (Exception e){
1075 throw new ApplicationException ("Color " + att + " is not a valid color.", e);
1078 // Should i also test for IsSystemColor?
1079 // Are KnownColor members in System.Drawing.Color?
1080 if (c.IsKnownColor){
1081 current_function.AppendFormat ("\t\t\t__ctrl.{0} = System.Drawing.Color." +
1082 "{1};\n", var_name, c.Name);
1085 current_function.AppendFormat ("\t\t\t__ctrl.{0} = System.Drawing.Color." +
1086 "FromArgb ({1}, {2}, {3}, {4});\n",
1087 var_name, c.A, c.R, c.G, c.B);
1090 else if (type == typeof (string [])) {
1091 string [] subStrings = att.Split (',');
1092 current_function.AppendFormat ("\t\t\t__ctrl.{0} = new String [] {{\n", var_name);
1093 int end = subStrings.Length;
1094 for (int i = 0; i < end; i++) {
1095 string s = subStrings [i].Trim ();
1096 current_function.AppendFormat ("\t\t\t\t\"{0}\"", s);
1098 current_function.Append ("\t\t\t\t};\n");
1100 current_function.Append (",\n");
1103 throw new ApplicationException ("Unsupported type in property: " +
1108 private bool ProcessPropertiesAndFields (MemberInfo member, string id, TagAttributes att)
1110 int hyphen = id.IndexOf ('-');
1112 bool isPropertyInfo = (member is PropertyInfo);
1114 bool is_processed = false;
1115 bool isDataBound = att.IsDataBound ((string) att [id]);
1117 if (isPropertyInfo) {
1118 type = ((PropertyInfo) member).PropertyType;
1119 if (hyphen == -1 && ((PropertyInfo) member).CanWrite == false)
1122 type = ((FieldInfo) member).FieldType;
1125 if (0 == String.Compare (member.Name, id, true)){
1126 AddCodeForPropertyOrField (type, member.Name, (string) att [id], isDataBound);
1127 is_processed = true;
1128 } else if (hyphen != -1 && (type == fontinfoType || type == styleType || type.IsSubclassOf (styleType))){
1129 string prop_field = id.Replace ("-", ".");
1130 string [] parts = prop_field.Split (new char [] {'.'});
1131 if (parts.Length != 2 || 0 != String.Compare (member.Name, parts [0], true))
1134 PropertyInfo [] subprops = type.GetProperties ();
1135 foreach (PropertyInfo subprop in subprops){
1136 if (0 != String.Compare (subprop.Name, parts [1], true))
1139 if (subprop.CanWrite == false)
1142 bool is_bool = subprop.PropertyType == typeof (bool);
1143 if (!is_bool && att [id] == null){
1144 att [id] = ""; // Font-Size -> Font-Size="" as html
1149 if (att [id] == null && is_bool)
1150 value = "true"; // Font-Bold <=> Font-Bold="true"
1152 value = (string) att [id];
1154 AddCodeForPropertyOrField (subprop.PropertyType,
1155 member.Name + "." + subprop.Name,
1156 value, isDataBound);
1157 is_processed = true;
1161 return is_processed;
1164 private void AddCodeForAttributes (Type type, TagAttributes att)
1166 EventInfo [] ev_info = type.GetEvents ();
1167 PropertyInfo [] prop_info = type.GetProperties ();
1168 FieldInfo [] field_info = type.GetFields ();
1169 bool is_processed = false;
1170 ArrayList processed = new ArrayList ();
1172 foreach (string id in att.Keys){
1173 if (0 == String.Compare (id, "runat", true) || 0 == String.Compare (id, "id", true))
1176 if (id.Length > 2 && id.Substring (0, 2).ToUpper () == "ON"){
1177 string id_as_event = id.Substring (2);
1178 foreach (EventInfo ev in ev_info){
1179 if (0 == String.Compare (ev.Name, id_as_event, true)){
1180 current_function.AppendFormat (
1181 "\t\t\t__ctrl.{0} += " +
1182 "new {1} (this.{2});\n",
1183 ev.Name, ev.EventHandlerType, att [id]);
1184 is_processed = true;
1189 is_processed = false;
1194 foreach (PropertyInfo prop in prop_info){
1195 is_processed = ProcessPropertiesAndFields (prop, id, att);
1200 if (!is_processed) {
1201 foreach (FieldInfo field in field_info){
1202 is_processed = ProcessPropertiesAndFields (field, id, att);
1209 is_processed = false;
1213 current_function.AppendFormat ("\t\t\t((System.Web.UI.IAttributeAccessor) __ctrl)." +
1214 "SetAttribute (\"{0}\", \"{1}\");\n",
1215 id, Escape ((string) att [id]));
1219 private void AddCodeRenderControl (StringBuilder function)
1221 AddCodeRenderControl (function, controls.CodeRenderIndex);
1224 private void AddCodeRenderControl (StringBuilder function, int index)
1226 function.AppendFormat ("\t\t\tparameterContainer.Controls [{0}]." +
1227 "RenderControl (__output);\n", index);
1230 private void AddRenderMethodDelegate (StringBuilder function, string control_id)
1232 function.AppendFormat ("\t\t\t__ctrl.SetRenderMethodDelegate (new System.Web." +
1233 "UI.RenderMethod (this.__Render_{0}));\n", control_id);
1236 private void AddCodeRenderFunction (string codeRender, string control_id)
1238 StringBuilder codeRenderFunction = new StringBuilder ();
1239 codeRenderFunction.AppendFormat ("\t\tprivate void __Render_{0} " +
1240 "(System.Web.UI.HtmlTextWriter __output, " +
1241 "System.Web.UI.Control parameterContainer)\n" +
1242 "\t\t{{\n", control_id);
1243 codeRenderFunction.Append (codeRender);
1244 codeRenderFunction.Append ("\t\t}\n\n");
1245 init_funcs.Append (codeRenderFunction);
1248 private void RemoveLiterals (StringBuilder function)
1250 string no_literals = Regex.Replace (function.ToString (),
1251 @"\t\t\t__parser.AddParsedSubObject \(" +
1252 @"new System.Web.UI.LiteralControl \(.+\);\n", "");
1253 function.Length = 0;
1254 function.Append (no_literals);
1257 private bool FinishControlFunction (string tag_id)
1259 if (functions.Count == 0)
1260 throw new ApplicationException ("Unbalanced open/close tags");
1262 if (controls.Count == 0)
1265 string saved_id = controls.PeekTagID ();
1266 if (0 != String.Compare (saved_id, tag_id, true))
1270 StringBuilder old_function = (StringBuilder) functions.Pop ();
1271 current_function = (StringBuilder) functions.Peek ();
1273 string control_id = controls.PeekControlID ();
1274 Type control_type = controls.PeekType ();
1275 ChildrenKind child_kind = controls.PeekChildKind ();
1277 bool hasDataBindFunction = controls.HasDataBindFunction ();
1278 if (hasDataBindFunction)
1279 old_function.AppendFormat ("\t\t\t__ctrl.DataBinding += new System.EventHandler " +
1280 "(this.__DataBind_{0});\n", control_id);
1282 bool useCodeRender = controls.UseCodeRender;
1284 AddRenderMethodDelegate (old_function, control_id);
1286 if (control_type == typeof (System.Web.UI.ITemplate)){
1287 old_function.Append ("\n\t\t}\n\n");
1288 current_function.AppendFormat ("\t\t\t__ctrl.{0} = new System.Web.UI." +
1289 "CompiledTemplateBuilder (new System.Web.UI." +
1290 "BuildTemplateMethod (this.__BuildControl_{1}));\n",
1291 saved_id, control_id);
1293 else if (control_type == typeof (System.Web.UI.WebControls.DataGridColumnCollection)){
1294 old_function.Append ("\n\t\t}\n\n");
1295 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n",
1296 control_id, saved_id);
1298 else if (control_type == typeof (System.Web.UI.WebControls.DataGridColumn) ||
1299 control_type.IsSubclassOf (typeof (System.Web.UI.WebControls.DataGridColumn)) ||
1300 control_type == typeof (System.Web.UI.WebControls.ListItem)){
1301 old_function.Append ("\n\t\t}\n\n");
1303 string ctrl_name = "ctrl";
1304 Type cont = controls.Container;
1305 if (cont == null || cont == typeof (System.Web.UI.HtmlControls.HtmlSelect)){
1306 parsed = "ParsedSubObject";
1307 ctrl_name = "parser";
1310 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n" +
1311 "\t\t\t__{1}.Add{2} (this.{0});\n\n",
1312 control_id, ctrl_name, parsed);
1314 else if (child_kind == ChildrenKind.LISTITEM){
1315 old_function.Append ("\n\t\t}\n\n");
1316 init_funcs.Append (old_function); // Closes the BuildList function
1317 old_function = (StringBuilder) functions.Pop ();
1318 current_function = (StringBuilder) functions.Peek ();
1319 old_function.AppendFormat ("\n\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n\t\t\t" +
1320 "return __ctrl;\n\t\t}}\n\n",
1321 control_id, controls.PeekDefaultPropertyName ());
1324 control_id = controls.PeekControlID ();
1325 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
1326 "AddParsedSubObject (this.{0});\n\n", control_id);
1327 } else if (control_type == typeof (HtmlTableCell) || control_type == typeof (TableCell)) {
1328 old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
1329 object top = controls.Pop ();
1330 Type t = controls.PeekType ();
1331 controls.Push (top);
1333 string ctrl_name = "ctrl";
1334 if (!t.IsSubclassOf (typeof (WebControl)) && t != typeof (HtmlTableRow)) {
1335 parsed = "ParsedSubObject";
1336 ctrl_name = "parser";
1339 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n" +
1340 "\t\t\t__{1}.Add{2} (this.{0});\n\n",
1341 control_id, ctrl_name, parsed);
1342 } else if (child_kind == ChildrenKind.HTMLROW || child_kind == ChildrenKind.HTMLCELL) {
1343 old_function.Append ("\n\t\t}\n\n");
1344 init_funcs.Append (old_function);
1345 old_function = (StringBuilder) functions.Pop ();
1346 current_function = (StringBuilder) functions.Peek ();
1347 old_function.AppendFormat ("\n\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n\t\t\t" +
1348 "return __ctrl;\n\t\t}}\n\n",
1349 control_id, controls.PeekDefaultPropertyName ());
1352 control_id = controls.PeekControlID ();
1353 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n", control_id);
1354 if (child_kind == ChildrenKind.HTMLROW) {
1355 current_function.AppendFormat ("\t\t\t__parser.AddParsedSubObject ({0});\n",
1358 current_function.AppendFormat ("\t\t\t__ctrl.Add (this.{0});\n", control_id);
1361 old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
1362 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
1363 "AddParsedSubObject (this.{0});\n\n", control_id);
1367 RemoveLiterals (old_function);
1369 init_funcs.Append (old_function);
1371 AddCodeRenderFunction (controls.CodeRenderFunction.ToString (), control_id);
1373 if (hasDataBindFunction){
1374 StringBuilder db_function = controls.DataBindFunction;
1375 db_function.Append ("\t\t}\n\n");
1376 init_funcs.Append (db_function);
1379 // Avoid getting empty stacks for unbalanced open/close tags
1380 if (controls.Count > 1){
1382 AddCodeRenderControl (controls.CodeRenderFunction, controls.ChildIndex);
1388 private void NewTableElementFunction (HtmlControlTag ctrl)
1390 string control_id = Tag.GetDefaultID ();
1391 ChildrenKind child_kind;
1394 if (ctrl.ControlType == typeof (HtmlTable)) {
1395 t = typeof (HtmlTableRowCollection);
1396 child_kind = ChildrenKind.HTMLROW;
1398 t = typeof (HtmlTableCellCollection);
1399 child_kind = ChildrenKind.HTMLCELL;
1402 controls.Push (ctrl.ControlType,
1406 ctrl.ParseChildren);
1408 current_function = new StringBuilder ();
1409 functions.Push (current_function);
1410 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ({1} __ctrl)\n" +
1411 "\t\t{{\n", control_id, t);
1414 private void ProcessHtmlControlTag ()
1417 HtmlControlTag html_ctrl = (HtmlControlTag) current;
1418 if (html_ctrl.TagID.ToUpper () == "SCRIPT"){
1419 //FIXME: if the is script is to be read from disk, do it!
1420 if (html_ctrl.SelfClosing)
1421 throw new ApplicationException ("Read script from file not supported yet.");
1423 sstatus = ScriptStatus.Open;
1428 throw new ApplicationException (app_file_wrong);
1430 Type controlType = html_ctrl.ControlType;
1431 AddProtectedField (controlType, html_ctrl.ControlID);
1433 ChildrenKind children_kind;
1434 if (0 == String.Compare (html_ctrl.TagID, "table", true))
1435 children_kind = ChildrenKind.HTMLROW;
1436 else if (0 == String.Compare (html_ctrl.TagID, "tr", true))
1437 children_kind = ChildrenKind.HTMLCELL;
1438 else if (0 != String.Compare (html_ctrl.TagID, "select", true))
1439 children_kind = html_ctrl.IsContainer ? ChildrenKind.CONTROLS :
1442 children_kind = ChildrenKind.OPTION;
1444 NewControlFunction (html_ctrl.TagID, html_ctrl.ControlID, controlType, children_kind, html_ctrl.ParseChildren);
1446 current_function.AppendFormat ("\t\t\t__ctrl.ID = \"{0}\";\n", html_ctrl.ControlID);
1447 AddCodeForAttributes (html_ctrl.ControlType, html_ctrl.Attributes);
1449 if (children_kind == ChildrenKind.HTMLROW || children_kind == ChildrenKind.HTMLCELL)
1450 NewTableElementFunction (html_ctrl);
1452 if (html_ctrl.SelfClosing)
1453 FinishControlFunction (html_ctrl.TagID);
1456 // Closing is performed in FinishControlFunction ()
1457 private void NewBuildListFunction (AspComponent component)
1459 string control_id = Tag.GetDefaultID ();
1461 controls.Push (component.ComponentType,
1464 component.ChildrenKind,
1465 component.DefaultPropertyName);
1467 Type childType = component.DefaultPropertyType;
1468 if (childType == null)
1469 childType = typeof (ListItemCollection);
1471 current_function = new StringBuilder ();
1472 functions.Push (current_function);
1473 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
1475 "\t\t{{\n", control_id, childType);
1478 private void ProcessComponent ()
1481 AspComponent component = (AspComponent) current;
1482 Type component_type = component.ComponentType;
1483 AddProtectedField (component_type, component.ControlID);
1485 NewControlFunction (component.TagID, component.ControlID, component_type,
1486 component.ChildrenKind, component.DefaultPropertyName);
1488 if (component_type == typeof (UserControl) ||
1489 component_type.IsSubclassOf (typeof (System.Web.UI.UserControl)))
1490 current_function.Append ("\t\t\t__ctrl.InitializeAsUserControl (Page);\n");
1492 if (component_type == typeof (Control) ||
1493 component_type.IsSubclassOf (typeof (System.Web.UI.Control)))
1494 current_function.AppendFormat ("\t\t\t__ctrl.ID = \"{0}\";\n", component.ControlID);
1496 AddCodeForAttributes (component.ComponentType, component.Attributes);
1497 if (component.ChildrenKind == ChildrenKind.LISTITEM || component.DefaultPropertyType != null)
1498 NewBuildListFunction (component);
1500 if (component.SelfClosing)
1501 FinishControlFunction (component.TagID);
1504 private void ProcessServerObjectTag ()
1507 ServerObjectTag obj = (ServerObjectTag) current;
1508 declarations.AppendFormat ("\t\tprivate {0} cached{1};\n", obj.ObjectClass, obj.ObjectID);
1509 constructor.AppendFormat ("\n\t\tprivate {0} {1}\n\t\t{{\n\t\t\tget {{\n\t\t\t\t" +
1510 "if (this.cached{1} == null)\n\t\t\t\t\tthis.cached{1} = " +
1511 "new {0} ();\n\t\t\t\treturn cached{1};\n\t\t\t}}\n\t\t}}\n\n",
1512 obj.ObjectClass, obj.ObjectID);
1515 // Creates a new function that sets the values of subproperties.
1516 private void NewStyleFunction (PropertyTag tag)
1518 current_function = new StringBuilder ();
1520 string prop_id = tag.PropertyID;
1521 Type prop_type = tag.PropertyType;
1523 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ({1} __ctrl)\n" +
1524 "\t\t{{\n", prop_id, prop_type);
1526 // Add property initialization code
1527 PropertyInfo [] subprop_info = prop_type.GetProperties ();
1528 TagAttributes att = tag.Attributes;
1530 string subprop_name = null;
1531 foreach (string id in att.Keys){
1532 if (0 == String.Compare (id, "runat", true) || 0 == String.Compare (id, "id", true))
1535 bool is_processed = false;
1536 foreach (PropertyInfo subprop in subprop_info){
1537 is_processed = ProcessPropertiesAndFields (subprop, id, att);
1539 subprop_name = subprop.Name;
1544 if (subprop_name == null)
1545 throw new ApplicationException ("Property " + tag.TagID + " does not have " +
1546 "a " + id + " subproperty.");
1550 current_function.Append ("\n\t\t}\n\n");
1551 init_funcs.Append (current_function);
1552 current_function = (StringBuilder) functions.Peek ();
1553 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} (__ctrl.{1});\n",
1554 prop_id, tag.PropertyName);
1556 if (!tag.SelfClosing){
1557 // Next tag should be the closing tag
1558 controls.Push (null, null, null, ChildrenKind.NONE, null);
1559 waitClosing = tag.TagID;
1563 // This one just opens the function. Closing is performed in FinishControlFunction ()
1564 private void NewTemplateFunction (PropertyTag tag)
1568 * This function does almost the same as NewControlFunction.
1571 string prop_id = tag.PropertyID;
1572 Type prop_type = tag.PropertyType;
1573 string tag_id = tag.PropertyName; // Real property name used in FinishControlFunction
1575 controls.Push (prop_type, prop_id, tag_id, ChildrenKind.CONTROLS, null);
1576 current_function = new StringBuilder ();
1577 functions.Push (current_function);
1578 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
1579 "(System.Web.UI.Control __ctrl)\n" +
1581 "\t\t\tSystem.Web.UI.IParserAccessor __parser " +
1582 "= (System.Web.UI.IParserAccessor) __ctrl;\n" , prop_id);
1585 // Closing is performed in FinishControlFunction ()
1586 private void NewDBColumnFunction (PropertyTag tag)
1590 * This function also does almost the same as NewControlFunction.
1593 string prop_id = tag.PropertyID;
1594 Type prop_type = tag.PropertyType;
1595 string tag_id = tag.PropertyName; // Real property name used in FinishControlFunction
1597 controls.Push (prop_type, prop_id, tag_id, ChildrenKind.DBCOLUMNS, null);
1598 current_function = new StringBuilder ();
1599 functions.Push (current_function);
1600 current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
1601 "(System.Web.UI.WebControls.DataGridColumnCollection __ctrl)\n" +
1602 "\t\t{{\n", prop_id);
1605 private void NewPropertyFunction (PropertyTag tag)
1608 if (tag.PropertyType == typeof (System.Web.UI.WebControls.Style) ||
1609 tag.PropertyType.IsSubclassOf (typeof (System.Web.UI.WebControls.Style)))
1610 NewStyleFunction (tag);
1611 else if (tag.PropertyType == typeof (System.Web.UI.ITemplate))
1612 NewTemplateFunction (tag);
1613 else if (tag.PropertyType == typeof (System.Web.UI.WebControls.DataGridColumnCollection))
1614 NewDBColumnFunction (tag);
1616 throw new ApplicationException ("Other than Style and ITemplate not supported yet. " +
1620 private void ProcessHtmlTag ()
1622 Tag tag = (Tag) current;
1623 ChildrenKind child_kind = controls.PeekChildKind ();
1624 if (child_kind == ChildrenKind.NONE){
1625 string tag_id = controls.PeekTagID ();
1626 throw new ApplicationException (tag + " not allowed inside " + tag_id);
1629 if (child_kind == ChildrenKind.OPTION){
1630 if (0 != String.Compare (tag.TagID, "option", true))
1631 throw new ApplicationException ("Only <option> tags allowed inside <select>.");
1633 string default_id = Tag.GetDefaultID ();
1634 Type type = typeof (System.Web.UI.WebControls.ListItem);
1635 AddProtectedField (type, default_id);
1636 NewControlFunction (tag.TagID, default_id, type, ChildrenKind.CONTROLS, null);
1640 if (child_kind == ChildrenKind.CONTROLS) {
1641 ArrayList tag_elements = tag.GetElements ();
1642 foreach (Element e in tag_elements) {
1643 if (e is PlainText) {
1645 textChunk.Append (((PlainText) e).Text);
1646 } else if (e is CodeRenderTag) {
1648 ProcessCodeRenderTag ();
1649 } else if (e is DataBindingTag) {
1651 ProcessDataBindingLiteral ();
1653 throw new ApplicationException (fullPath + ": unexpected tag type " + e.GetType ());
1659 if (child_kind == ChildrenKind.HTMLROW) {
1660 if (0 == String.Compare (tag.TagID, "tr", true)) {
1661 current = new HtmlControlTag (tag);
1662 ProcessHtmlControlTag ();
1667 if (child_kind == ChildrenKind.HTMLCELL) {
1668 if (0 == String.Compare (tag.TagID, "td", true)) {
1669 current = new HtmlControlTag (tag);
1670 ProcessHtmlControlTag ();
1675 // Now child_kind should be PROPERTIES, so only allow tag_id == property
1676 Type control_type = controls.PeekType ();
1677 PropertyInfo [] prop_info = control_type.GetProperties ();
1678 bool is_processed = false;
1679 foreach (PropertyInfo prop in prop_info){
1680 if (0 == String.Compare (prop.Name, tag.TagID, true)){
1681 PropertyTag prop_tag = new PropertyTag (tag, prop.PropertyType, prop.Name);
1682 NewPropertyFunction (prop_tag);
1683 is_processed = true;
1689 string tag_id = controls.PeekTagID ();
1690 throw new ApplicationException (tag.TagID + " is not a property of " + control_type);
1694 private Tag Map (Tag tag)
1696 int pos = tag.TagID.IndexOf (":");
1698 ChildrenKind child_kind = controls.PeekChildKind ();
1699 if (child_kind == ChildrenKind.HTMLROW && 0 == String.Compare (tag.TagID, "tr", true)) {
1700 tag.Attributes.Add ("runat", "server");
1701 return new HtmlControlTag (tag);
1702 } else if (child_kind == ChildrenKind.HTMLROW && 0 == String.Compare (tag.TagID, "tr", true)) {
1703 tag.Attributes.Add ("runat", "server");
1704 return new HtmlControlTag (tag);
1708 if (tag is CloseTag ||
1709 ((tag.Attributes == null ||
1710 !tag.Attributes.IsRunAtServer ()) && pos == -1))
1714 if (0 == String.Compare (tag.TagID, "object", true))
1715 return new ServerObjectTag (tag);
1717 return new HtmlControlTag (tag);
1720 string foundry_name = tag.TagID.Substring (0, pos);
1721 string component_name = tag.TagID.Substring (pos + 1);
1723 if (Foundry.LookupFoundry (foundry_name) == false)
1724 throw new ApplicationException ("Cannot find foundry for alias'" + foundry_name + "'");
1726 AspComponent component = Foundry.MakeAspComponent (foundry_name, component_name, tag);
1727 if (component == null)
1728 throw new ApplicationException ("Cannot find component '" + component_name +
1729 "' for alias '" + foundry_name + "'");
1734 private void ProcessCloseTag ()
1736 CloseTag closeTag = (CloseTag) current;
1737 if (FinishControlFunction (closeTag.TagID))
1740 textChunk.Append (closeTag.PlainHtml);
1743 private void ProcessDataBindingLiteral ()
1746 DataBindingTag dataBinding = (DataBindingTag) current;
1747 string actual_value = dataBinding.Data;
1748 if (actual_value == "")
1749 throw new ApplicationException ("Empty data binding tag.");
1751 if (controls.PeekChildKind () != ChildrenKind.CONTROLS)
1752 throw new ApplicationException ("Data bound content not allowed for " +
1753 controls.PeekTagID ());
1755 StringBuilder db_function = new StringBuilder ();
1756 string control_id = Tag.GetDefaultID ();
1757 string control_type_string = "System.Web.UI.DataBoundLiteralControl";
1758 AddProtectedField (typeof (System.Web.UI.DataBoundLiteralControl), control_id);
1759 // Build the control
1760 db_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_{0} ()\n" +
1761 "\t\t{{\n\t\t\t{1} __ctrl;\n\n" +
1762 "\t\t\t__ctrl = new {1} (0, 1);\n" +
1763 "\t\t\tthis.{0} = __ctrl;\n" +
1764 "\t\t\t__ctrl.DataBinding += new System.EventHandler " +
1765 "(this.__DataBind_{0});\n" +
1766 "\t\t\treturn __ctrl;\n"+
1768 control_id, control_type_string);
1769 // DataBinding handler
1770 db_function.AppendFormat ("\t\tpublic void __DataBind_{0} (object sender, " +
1771 "System.EventArgs e) {{\n" +
1772 "\t\t\t{1} Container;\n" +
1773 "\t\t\t{2} target;\n" +
1774 "\t\t\ttarget = ({2}) sender;\n" +
1775 "\t\t\tContainer = ({1}) target.BindingContainer;\n" +
1776 "\t\t\ttarget.SetDataBoundString (0, System.Convert." +
1777 "ToString ({3}));\n" +
1779 control_id, controls.Container, control_type_string,
1782 init_funcs.Append (db_function);
1783 current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
1784 "AddParsedSubObject (this.{0});\n\n", control_id);
1786 AddCodeRenderControl (controls.CodeRenderFunction);
1789 private void ProcessCodeRenderTag ()
1792 CodeRenderTag code_tag = (CodeRenderTag) current;
1794 controls.UseCodeRender = true;
1795 if (code_tag.IsVarName)
1796 controls.CodeRenderFunction.AppendFormat ("\t\t\t__output.Write ({0});\n",
1799 controls.CodeRenderFunction.AppendFormat ("\t\t\t{0}\n", code_tag.Code);
1802 void FlushPlainText ()
1804 if (textChunk.Length != 0) {
1805 Element saved = current;
1806 current = new PlainText (textChunk.ToString ());
1807 textChunk.Length = 0;
1808 ProcessPlainText ();
1813 void ParseError (string msg, int line, int col)
1815 throw new ParseException (fullPath, msg, line, col);
1818 void TagParsed (Tag tag, int line, int col)
1820 if (waitClosing != null) {
1821 if (!(tag is CloseTag) || tag.TagID.ToUpper () != waitClosing.ToUpper ())
1822 throw new HttpException ("Tag " + waitClosing + " not properly closed.");
1829 if ((sstatus == ScriptStatus.Open || sstatus == ScriptStatus.Text) &&
1830 tag.TagID.ToUpper () == "SCRIPT" && tag is CloseTag) {
1831 sstatus = ScriptStatus.Close;
1837 if (current is Directive) {
1838 ProcessDirective ();
1840 } else if (current is DataBindingTag) {
1842 throw new ApplicationException (app_file_wrong);
1844 ProcessDataBindingLiteral ();
1846 } else if (current is CodeRenderTag) {
1848 throw new ApplicationException (app_file_wrong);
1850 ProcessCodeRenderTag ();
1854 current = Map (tag);
1856 if (current is ServerObjectTag) {
1857 ProcessServerObjectTag ();
1859 } else if (current is HtmlControlTag) {
1860 ProcessHtmlControlTag ();
1865 throw new ApplicationException (app_file_wrong);
1867 if (current is AspComponent) {
1868 ProcessComponent ();
1869 } else if (current is CloseTag) {
1871 } else if (current is Tag) {
1874 throw new HttpException ("This place should not be reached.");
1878 void TextParsed (string text, int line, int col)
1880 if (sstatus == ScriptStatus.Open) {
1881 script.Append (text);
1882 sstatus = ScriptStatus.Text;
1886 textChunk.Append (text);
1889 public void ProcessElements ()
1891 AspParser parser = new AspParser (fullPath, File.OpenRead (fullPath));
1893 parser.Error += new ParseErrorHandler (ParseError);
1894 parser.TagParsed += new TagParsedHandler (TagParsed);
1895 parser.TextParsed += new TextParsedHandler (TextParsed);
1899 } catch (CompilationException e) {
1901 } catch (ParseException e) {
1903 } catch (Exception e) {
1904 throw new ParseException (fullPath, e.Message, parser.Line, parser.Column, e);
1911 private string GetTemplateDirectory ()
1913 string templatePath = Path.GetDirectoryName (fullPath);
1914 string appPath = Path.GetDirectoryName (HttpRuntime.AppDomainAppPath);
1916 if (templatePath == appPath)
1919 templatePath = templatePath.Substring (appPath.Length);
1920 if (Path.DirectorySeparatorChar != '/')
1921 templatePath = templatePath.Replace (Path.DirectorySeparatorChar, '/');
1923 return templatePath;
1931 if (sessionState == SessionState.Enabled || sessionState == SessionState.ReadOnly)
1932 AddInterface (typeof (System.Web.SessionState.IRequiresSessionState));
1934 if (sessionState == SessionState.ReadOnly)
1935 AddInterface (typeof (System.Web.SessionState.IReadOnlySessionState));
1938 classDecl = "\tpublic class " + className + " : " + parent + interfaces + " {\n";
1939 prolog.Append ("\n" + classDecl);
1940 declarations.Append ("\t\tprivate static bool __intialized = false;\n\n");
1942 declarations.Append ("\t\tprivate static ArrayList __fileDependencies;\n\n");
1944 // adds the constructor
1945 constructor.AppendFormat ("\t\tpublic {0} ()\n\t\t{{\n", className);
1947 constructor.Append ("\t\t\tSystem.Collections.ArrayList dependencies;\n\n");
1949 constructor.AppendFormat ("\t\t\tif (ASP.{0}.__intialized == false){{\n", className);
1952 constructor.AppendFormat ("\t\t\t\tdependencies = new System.Collections.ArrayList ();\n" +
1953 "\t\t\t\tdependencies.Add (@\"{1}\");\n" +
1954 "\t\t\t\tASP.{0}.__fileDependencies = dependencies;\n",
1955 className, fullPath);
1958 constructor.AppendFormat ("\t\t\t\tASP.{0}.__intialized = true;\n\t\t\t}}\n\t\t}}\n\n",
1961 if (!IsApplication) {
1962 //FIXME: add AutoHandlers: don't know what for...yet!
1963 constructor.AppendFormat (
1964 "\t\tprotected override int AutoHandlers\n\t\t{{\n" +
1965 "\t\t\tget {{ return ASP.{0}.__autoHandlers; }}\n" +
1966 "\t\t\tset {{ ASP.{0}.__autoHandlers = value; }}\n" +
1967 "\t\t}}\n\n", className);
1969 constructor.Append (
1970 "\t\tprotected System.Web.HttpApplication ApplicationInstance\n\t\t{\n" +
1971 "\t\t\tget { return (System.Web.HttpApplication) this.Context.ApplicationInstance; }\n" +
1974 constructor.AppendFormat (
1975 "\t\tpublic override string TemplateSourceDirectory\n\t\t{{\n" +
1976 "\t\t\tget {{ return \"{0}\"; }}\n" +
1977 "\t\t}}\n\n", GetTemplateDirectory ());
1979 epilog.Append ("\n\t\tprotected override void FrameworkInitialize ()\n\t\t{\n" +
1980 "\t\t\tthis.__BuildControlTree (this);\n");
1983 epilog.AppendFormat ("\t\t\tthis.FileDependencies = ASP.{0}.__fileDependencies;\n" +
1984 "\t\t\tthis.EnableViewStateMac = true;\n", className);
1987 epilog.Append ("\t\t}\n\n");
1991 Random rnd = new Random ();
1992 epilog.AppendFormat ("\t\tpublic override int GetTypeHashCode ()\n\t\t{{\n" +
1993 "\t\t\treturn {0};\n" +
1994 "\t\t}}\n", rnd.Next ());
1997 epilog.Append ("\t}\n}\n");
1999 // Closes the currently opened tags
2000 StringBuilder old_function = current_function;
2002 while (functions.Count > 1){
2003 old_function.Append ("\n\t\t\treturn __ctrl;\n\t\t}\n\n");
2004 init_funcs.Append (old_function);
2005 control_id = controls.PeekControlID ();
2006 FinishControlFunction (control_id);
2007 controls.AddChild ();
2008 old_function = (StringBuilder) functions.Pop ();
2009 current_function = (StringBuilder) functions.Peek ();
2013 bool useCodeRender = controls.UseCodeRender;
2015 RemoveLiterals (current_function);
2016 AddRenderMethodDelegate (current_function, controls.PeekControlID ());
2019 current_function.Append ("\t\t}\n\n");
2020 init_funcs.Append (current_function);
2022 AddCodeRenderFunction (controls.CodeRenderFunction.ToString (), controls.PeekControlID ());
2028 // Functions related to compilation of user controls
2031 private static char dirSeparator = Path.DirectorySeparatorChar;
2032 struct UserControlData
2034 public UserControlResult result;
2035 public string className;
2036 public string assemblyName;
2039 private UserControlData GenerateUserControl (string src, HttpContext context)
2041 UserControlData data = new UserControlData ();
2042 data.result = UserControlResult.OK;
2044 UserControlCompiler compiler = new UserControlCompiler (new UserControlParser (src, context));
2045 Type t = compiler.GetCompiledType ();
2047 data.result = UserControlResult.CompilationFailed;
2051 data.className = t.Name;
2052 data.assemblyName = compiler.TargetFile;
2053 dependencies.Add (src);
2054 foreach (string s in compiler.Dependencies)
2055 dependencies.Add (s);