class status fixes
[mono.git] / mcs / class / System.Web / System.Web.UI / ControlBuilder.cs
1 //
2 // System.Web.UI.ControlBuilder.cs
3 //
4 // Authors:
5 //      Duncan Mak  (duncan@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (C) 2002, 2003 Ximian, Inc. (http://www.ximian.com)
9 //
10
11 using System;
12 using System.Collections;
13 using System.CodeDom;
14 using System.Reflection;
15 using System.Web;
16 using System.Web.Compilation;
17
18 namespace System.Web.UI {
19
20         public class ControlBuilder
21         {
22                 internal static BindingFlags flagsNoCase = BindingFlags.Public |
23                                                            BindingFlags.Instance |
24                                                            BindingFlags.Static |
25                                                            BindingFlags.IgnoreCase;
26
27                 TemplateParser parser;
28                 internal ControlBuilder parentBuilder;
29                 Type type;             
30                 string tagName;
31                 string id;
32                 internal IDictionary attribs;
33                 internal int line;
34                 internal string fileName;
35                 bool childrenAsProperties;
36                 bool isIParserAccessor = true;
37                 bool hasAspCode;
38                 internal ControlBuilder defaultPropertyBuilder;
39                 ArrayList children;
40                 static int nextID;
41
42                 internal bool haveParserVariable;
43                 internal CodeMemberMethod method;
44                 internal CodeMemberMethod renderMethod;
45                 internal int renderIndex;
46                 internal bool isProperty;
47                 internal ILocation location;
48
49                 public ControlBuilder ()
50                 {
51                 }
52
53                 internal ControlBuilder (TemplateParser parser,
54                                          ControlBuilder parentBuilder,
55                                          Type type,
56                                          string tagName,
57                                          string id,
58                                          IDictionary attribs,
59                                          int line,
60                                          string sourceFileName)
61
62                 {
63                         this.parser = parser;
64                         this.parentBuilder = parentBuilder;
65                         this.type = type;
66                         this.tagName = tagName;
67                         this.id = id;
68                         this.attribs = attribs;
69                         this.line = line;
70                         this.fileName = sourceFileName;
71                 }
72
73                 public Type ControlType {
74                         get { return type; }
75                 }
76
77                 protected bool FChildrenAsProperties {
78                         get { return childrenAsProperties; }
79                 }
80
81                 protected bool FIsNonParserAccessor {
82                         get { return !isIParserAccessor; }
83                 }
84
85                 public bool HasAspCode {
86                         get { return hasAspCode; }
87                 }
88
89                 public string ID {
90                         get { return id; }
91                         set { id = value; }
92                 }
93
94                 internal ArrayList Children {
95                         get { return children; }
96                 }
97
98                 internal void SetControlType (Type t)
99                 {
100                         type = t;
101                 }
102                 
103                 protected bool InDesigner {
104                         get { return false; }
105                 }
106
107                 public Type NamingContainerType {
108                         get {
109                                 if (parentBuilder == null)
110                                         return typeof (Control);
111
112                                 Type ptype = parentBuilder.ControlType;
113                                 if (ptype == null)
114                                         return parentBuilder.NamingContainerType;
115
116                                 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
117                                         return parentBuilder.NamingContainerType;
118
119                                 return ptype;
120                         }
121                 }
122
123                 protected TemplateParser Parser {
124                         get { return parser; }
125                 }
126
127                 public string TagName {
128                         get { return tagName; }
129                 }
130
131                 internal RootBuilder Root {
132                         get {
133                                 if (GetType () == typeof (RootBuilder))
134                                         return (RootBuilder) this;
135
136                                 return (RootBuilder) parentBuilder.Root;
137                         }
138                 }
139
140                 internal bool ChildrenAsProperties {
141                         get { return childrenAsProperties; }
142                 }
143                 
144                 public virtual bool AllowWhitespaceLiterals ()
145                 {
146                         return true;
147                 }
148
149                 public virtual void AppendLiteralString (string s)
150                 {
151                         if (s == null || s == "")
152                                 return;
153
154                         if (childrenAsProperties || !isIParserAccessor) {
155                                 if (defaultPropertyBuilder != null) {
156                                         defaultPropertyBuilder.AppendLiteralString (s);
157                                 } else if (s.Trim () != "") {
158                                         throw new HttpException ("Literal content not allowed for " + tagName + " " +
159                                                                 GetType () + " \"" + s + "\"");
160                                 }
161
162                                 return;
163                         }
164                         
165                         if (!AllowWhitespaceLiterals () && s.Trim () == "")
166                                 return;
167
168                         if (HtmlDecodeLiterals ())
169                                 s = HttpUtility.HtmlDecode (s);
170
171                         if (children == null)
172                                 children = new ArrayList ();
173
174                         children.Add (s);
175                 }
176
177                 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
178                 {
179                         subBuilder.OnAppendToParentBuilder (this);
180                         
181                         subBuilder.parentBuilder = this;
182                         if (childrenAsProperties) {
183                                 AppendToProperty (subBuilder);
184                                 return;
185                         }
186
187                         if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
188                                 AppendCode (subBuilder);
189                                 return;
190                         }
191
192                         if (children == null)
193                                 children = new ArrayList ();
194
195                         children.Add (subBuilder);
196                 }
197
198                 void AppendToProperty (ControlBuilder subBuilder)
199                 {
200                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
201                                 throw new HttpException ("Code render not supported here.");
202
203                         if (defaultPropertyBuilder != null) {
204                                 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
205                                 return;
206                         }
207
208                         if (children == null)
209                                 children = new ArrayList ();
210
211                         children.Add (subBuilder);
212                 }
213
214                 void AppendCode (ControlBuilder subBuilder)
215                 {
216                         if (type != null && !(typeof (Control).IsAssignableFrom (type)))
217                                 throw new HttpException ("Code render not supported here.");
218
219                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
220                                 hasAspCode = true;
221
222                         if (children == null)
223                                 children = new ArrayList ();
224
225                         children.Add (subBuilder);
226                 }
227
228                 public virtual void CloseControl ()
229                 {
230                 }
231
232                 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
233                                                                     ControlBuilder parentBuilder,
234                                                                     Type type,
235                                                                     string tagName,
236                                                                     string id,
237                                                                     IDictionary attribs,
238                                                                     int line,
239                                                                     string sourceFileName)
240                 {
241                         ControlBuilder  builder;
242                         object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
243                         if (atts != null && atts.Length > 0) {
244                                 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
245                                 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
246                         } else {
247                                 builder = new ControlBuilder ();
248                         }
249
250                         builder.Init (parser, parentBuilder, type, tagName, id, attribs);
251                         builder.line = line;
252                         builder.fileName = sourceFileName;
253                         return builder;
254                 }
255
256                 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
257                 {
258                         return null;
259                 }
260
261                 public virtual bool HasBody ()
262                 {
263                         return true;
264                 }
265
266                 public virtual bool HtmlDecodeLiterals ()
267                 {
268                         return false;
269                 }
270
271                 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
272                 {
273                         PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
274                         if (prop == null) {
275                                 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
276                                 throw new HttpException (msg);
277                         }
278
279                         Type propType = prop.PropertyType;
280                         ControlBuilder builder = null;
281                         if (typeof (ICollection).IsAssignableFrom (propType)) {
282                                 builder = new CollectionBuilder ();
283                         } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
284                                 builder = new TemplateBuilder ();
285                         } else {
286                                 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
287                                                                  null, atts, line, fileName);
288                                 builder.isProperty = true;
289                                 return builder;
290                         }
291
292                         builder.Init (parser, this, null, prop.Name, null, atts);
293                         builder.fileName = fileName;
294                         builder.line = line;
295                         builder.isProperty = true;
296                         return builder;
297                 }
298                 
299                 public virtual void Init (TemplateParser parser,
300                                           ControlBuilder parentBuilder,
301                                           Type type,
302                                           string tagName,
303                                           string id,
304                                           IDictionary attribs)
305                 {
306                         this.parser = parser;
307                         if (parser != null)
308                                 this.location = parser.Location;
309
310                         this.parentBuilder = parentBuilder;
311                         this.type = type;
312                         this.tagName = tagName;
313                         this.id = id;
314                         this.attribs = attribs;
315                         if (type == null)
316                                 return;
317
318                         if (this is TemplateBuilder)
319                                 return;
320
321                         if (!typeof (IParserAccessor).IsAssignableFrom (type)) {
322                                 isIParserAccessor = false;
323                                 childrenAsProperties = true;
324                         } else {
325                                 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
326                                 if (atts != null && atts.Length > 0) {
327                                         ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
328                                         childrenAsProperties = att.ChildrenAsProperties;
329                                         if (childrenAsProperties && att.DefaultProperty != "") {
330                                                 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
331                                                                                                 parser, null);
332                                         }
333                                 }
334                         }
335                 }
336
337                 public virtual bool NeedsTagInnerText ()
338                 {
339                         return false;
340                 }
341
342                 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
343                 {
344                         if (defaultPropertyBuilder == null)
345                                 return;
346
347                         ControlBuilder old = defaultPropertyBuilder;
348                         defaultPropertyBuilder = null;
349                         AppendSubBuilder (old);
350                 }
351
352                 public virtual void SetTagInnerText (string text)
353                 {
354                 }
355
356                 internal string GetNextID (string proposedID)
357                 {
358                         if (proposedID != null && proposedID.Trim () != "")
359                                 return proposedID;
360
361                         return "_bctrl_" + nextID++;
362                 }
363
364                 internal virtual ControlBuilder CreateSubBuilder (string tagid,
365                                                                   Hashtable atts,
366                                                                   Type childType,
367                                                                   TemplateParser parser,
368                                                                   ILocation location)
369                 {
370                         ControlBuilder childBuilder = null;
371                         if (childrenAsProperties) {
372                                 if (defaultPropertyBuilder == null) {
373                                         childBuilder = CreatePropertyBuilder (tagid, parser, atts);
374                                 } else {
375                                         childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
376                                                                                         null, parser, location);
377                                 }
378                                 return childBuilder;
379                         }
380
381                         childType = GetChildControlType (tagid, atts);
382                         if (childType == null)
383                                 return null;
384
385                         childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
386                                                               location.BeginLine, location.Filename);
387
388                         return childBuilder;
389                 }
390
391                 internal virtual object CreateInstance ()
392                 {
393                         // HtmlGenericControl, HtmlTableCell...
394                         object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
395                         object [] args = null;
396                         if (atts != null && atts.Length > 0) {
397                                 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
398                                 if (att.NeedsTag)
399                                         args = new object [] {tagName};
400                         }
401
402                         return Activator.CreateInstance (type, args);
403                 }
404
405                 internal virtual void CreateChildren (object parent) 
406                 {
407                         if (children == null || children.Count == 0)
408                                 return;
409
410                         IParserAccessor parser = parent as IParserAccessor;
411                         if (parser == null)
412                                 return;
413
414                         foreach (object o in children) {
415                                 if (o is string) {
416                                         parser.AddParsedSubObject (new LiteralControl ((string) o));
417                                 } else {
418                                         parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
419                                 }
420                         }
421                 }
422         }
423 }
424