Patch by D. Moonfire that fixes several issues with 2.0 page attributes.
[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 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Collections;
32 using System.CodeDom;
33 using System.Reflection;
34 using System.Security.Permissions;
35 using System.Web.Compilation;
36
37 namespace System.Web.UI {
38
39         // CAS
40         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
42         public class ControlBuilder
43         {
44                 internal static BindingFlags flagsNoCase = BindingFlags.Public |
45                                                            BindingFlags.Instance |
46                                                            BindingFlags.Static |
47                                                            BindingFlags.IgnoreCase;
48
49                 TemplateParser parser;
50                 internal ControlBuilder parentBuilder;
51                 Type type;             
52                 string tagName;
53                 string id;
54                 internal IDictionary attribs;
55                 internal int line;
56                 internal string fileName;
57                 bool childrenAsProperties;
58                 bool isIParserAccessor = true;
59                 bool hasAspCode;
60                 internal ControlBuilder defaultPropertyBuilder;
61                 ArrayList children;
62                 static int nextID;
63
64                 internal bool haveParserVariable;
65                 internal CodeMemberMethod method;
66                 internal CodeMemberMethod renderMethod;
67                 internal int renderIndex;
68                 internal bool isProperty;
69                 internal ILocation location;
70                 ArrayList otherTags;
71
72                 public ControlBuilder ()
73                 {
74                 }
75
76                 internal ControlBuilder (TemplateParser parser,
77                                          ControlBuilder parentBuilder,
78                                          Type type,
79                                          string tagName,
80                                          string id,
81                                          IDictionary attribs,
82                                          int line,
83                                          string sourceFileName)
84
85                 {
86                         this.parser = parser;
87                         this.parentBuilder = parentBuilder;
88                         this.type = type;
89                         this.tagName = tagName;
90                         this.id = id;
91                         this.attribs = attribs;
92                         this.line = line;
93                         this.fileName = sourceFileName;
94                 }
95
96                 internal void EnsureOtherTags ()
97                 {
98                         if (otherTags == null)
99                                 otherTags = new ArrayList ();
100                 }
101                 
102                 internal ArrayList OtherTags {
103                         get { return otherTags; }
104                 }
105
106                 public Type ControlType {
107                         get { return type; }
108                 }
109
110                 protected bool FChildrenAsProperties {
111                         get { return childrenAsProperties; }
112                 }
113
114                 protected bool FIsNonParserAccessor {
115                         get { return !isIParserAccessor; }
116                 }
117
118                 public bool HasAspCode {
119                         get { return hasAspCode; }
120                 }
121
122                 public string ID {
123                         get { return id; }
124                         set { id = value; }
125                 }
126
127                 internal ArrayList Children {
128                         get { return children; }
129                 }
130
131                 internal void SetControlType (Type t)
132                 {
133                         type = t;
134                 }
135                 
136                 protected bool InDesigner {
137                         get { return false; }
138                 }
139
140                 public Type NamingContainerType {
141                         get {
142                                 if (parentBuilder == null)
143                                         return typeof (Control);
144
145                                 Type ptype = parentBuilder.ControlType;
146                                 if (ptype == null)
147                                         return parentBuilder.NamingContainerType;
148
149                                 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
150                                         return parentBuilder.NamingContainerType;
151
152                                 return ptype;
153                         }
154                 }
155
156 #if NET_2_0
157                 public
158 #else
159                 internal
160 #endif
161                 Type BindingContainerType {
162                         get {
163                                 if (parentBuilder == null)
164                                         return typeof (Control);
165                                         
166                                 if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
167                                         return ((TemplateBuilder)parentBuilder).ContainerType;
168
169                                 Type ptype = parentBuilder.ControlType;
170                                 if (ptype == null)
171                                         return parentBuilder.BindingContainerType;
172
173                                 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
174                                         return parentBuilder.BindingContainerType;
175
176                                 return ptype;
177                         }
178                 }
179
180                 internal TemplateBuilder ParentTemplateBuilder {
181                         get {
182                                 if (parentBuilder == null)
183                                         return null;
184                                 else if (parentBuilder is TemplateBuilder)
185                                         return (TemplateBuilder) parentBuilder;
186                                 else
187                                         return parentBuilder.ParentTemplateBuilder;
188                         }
189                 }
190
191                 protected TemplateParser Parser {
192                         get { return parser; }
193                 }
194
195                 public string TagName {
196                         get { return tagName; }
197                 }
198
199                 internal RootBuilder Root {
200                         get {
201                                 if (GetType () == typeof (RootBuilder))
202                                         return (RootBuilder) this;
203
204                                 return (RootBuilder) parentBuilder.Root;
205                         }
206                 }
207
208                 internal bool ChildrenAsProperties {
209                         get { return childrenAsProperties; }
210                 }
211                 
212                 public virtual bool AllowWhitespaceLiterals ()
213                 {
214                         return true;
215                 }
216
217                 public virtual void AppendLiteralString (string s)
218                 {
219                         if (s == null || s == "")
220                                 return;
221
222                         if (childrenAsProperties || !isIParserAccessor) {
223                                 if (defaultPropertyBuilder != null) {
224                                         defaultPropertyBuilder.AppendLiteralString (s);
225                                 } else if (s.Trim () != "") {
226                                         throw new HttpException ("Literal content not allowed for " + tagName + " " +
227                                                                 GetType () + " \"" + s + "\"");
228                                 }
229
230                                 return;
231                         }
232                         
233                         if (!AllowWhitespaceLiterals () && s.Trim () == "")
234                                 return;
235
236                         if (HtmlDecodeLiterals ())
237                                 s = HttpUtility.HtmlDecode (s);
238
239                         if (children == null)
240                                 children = new ArrayList ();
241
242                         children.Add (s);
243                 }
244
245                 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
246                 {
247                         subBuilder.OnAppendToParentBuilder (this);
248                         
249                         subBuilder.parentBuilder = this;
250                         if (childrenAsProperties) {
251                                 AppendToProperty (subBuilder);
252                                 return;
253                         }
254
255                         if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
256                                 AppendCode (subBuilder);
257                                 return;
258                         }
259
260                         if (children == null)
261                                 children = new ArrayList ();
262
263                         children.Add (subBuilder);
264                 }
265
266                 void AppendToProperty (ControlBuilder subBuilder)
267                 {
268                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
269                                 throw new HttpException ("Code render not supported here.");
270
271                         if (defaultPropertyBuilder != null) {
272                                 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
273                                 return;
274                         }
275
276                         if (children == null)
277                                 children = new ArrayList ();
278
279                         children.Add (subBuilder);
280                 }
281
282                 void AppendCode (ControlBuilder subBuilder)
283                 {
284                         if (type != null && !(typeof (Control).IsAssignableFrom (type)))
285                                 throw new HttpException ("Code render not supported here.");
286
287                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
288                                 hasAspCode = true;
289
290                         if (children == null)
291                                 children = new ArrayList ();
292
293                         children.Add (subBuilder);
294                 }
295
296                 public virtual void CloseControl ()
297                 {
298                 }
299
300                 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
301                                                                     ControlBuilder parentBuilder,
302                                                                     Type type,
303                                                                     string tagName,
304                                                                     string id,
305                                                                     IDictionary attribs,
306                                                                     int line,
307                                                                     string sourceFileName)
308                 {
309                         ControlBuilder  builder;
310                         object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
311                         if (atts != null && atts.Length > 0) {
312                                 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
313                                 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
314                         } else {
315                                 builder = new ControlBuilder ();
316                         }
317
318                         builder.Init (parser, parentBuilder, type, tagName, id, attribs);
319                         builder.line = line;
320                         builder.fileName = sourceFileName;
321                         return builder;
322                 }
323
324                 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
325                 {
326                         return null;
327                 }
328
329                 public virtual bool HasBody ()
330                 {
331                         return true;
332                 }
333
334                 public virtual bool HtmlDecodeLiterals ()
335                 {
336                         return false;
337                 }
338
339                 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
340                 {
341                         PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
342                         if (prop == null) {
343                                 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
344                                 throw new HttpException (msg);
345                         }
346
347                         Type propType = prop.PropertyType;
348                         ControlBuilder builder = null;
349                         if (typeof (ICollection).IsAssignableFrom (propType)) {
350                                 builder = new CollectionBuilder ();
351                         } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
352                                 builder = new TemplateBuilder (prop);
353                         } else {
354                                 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
355                                                                  null, atts, line, fileName);
356                                 builder.isProperty = true;
357                                 return builder;
358                         }
359
360                         builder.Init (parser, this, null, prop.Name, null, atts);
361                         builder.fileName = fileName;
362                         builder.line = line;
363                         builder.isProperty = true;
364                         return builder;
365                 }
366                 
367                 public virtual void Init (TemplateParser parser,
368                                           ControlBuilder parentBuilder,
369                                           Type type,
370                                           string tagName,
371                                           string id,
372                                           IDictionary attribs)
373                 {
374                         this.parser = parser;
375                         if (parser != null)
376                                 this.location = parser.Location;
377
378                         this.parentBuilder = parentBuilder;
379                         this.type = type;
380                         this.tagName = tagName;
381                         this.id = id;
382                         this.attribs = attribs;
383                         if (type == null)
384                                 return;
385
386                         if (this is TemplateBuilder)
387                                 return;
388
389                         object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
390                         
391                         if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
392                                 isIParserAccessor = false;
393                                 childrenAsProperties = true;
394                         } else if (atts.Length > 0) {
395                                 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
396                                 childrenAsProperties = att.ChildrenAsProperties;
397                                 if (childrenAsProperties && att.DefaultProperty != "") {
398                                         defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
399                                                                                         parser, null);
400                                 }
401                         }
402                 }
403
404                 public virtual bool NeedsTagInnerText ()
405                 {
406                         return false;
407                 }
408
409                 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
410                 {
411                         if (defaultPropertyBuilder == null)
412                                 return;
413
414                         ControlBuilder old = defaultPropertyBuilder;
415                         defaultPropertyBuilder = null;
416                         AppendSubBuilder (old);
417                 }
418
419                 internal void SetTagName (string name)
420                 {
421                         tagName = name;
422                 }
423                 
424                 public virtual void SetTagInnerText (string text)
425                 {
426                 }
427
428                 internal string GetNextID (string proposedID)
429                 {
430                         if (proposedID != null && proposedID.Trim () != "")
431                                 return proposedID;
432
433                         return "_bctrl_" + nextID++;
434                 }
435
436                 internal virtual ControlBuilder CreateSubBuilder (string tagid,
437                                                                   Hashtable atts,
438                                                                   Type childType,
439                                                                   TemplateParser parser,
440                                                                   ILocation location)
441                 {
442                         ControlBuilder childBuilder = null;
443                         if (childrenAsProperties) {
444                                 if (defaultPropertyBuilder == null) {
445                                         childBuilder = CreatePropertyBuilder (tagid, parser, atts);
446                                 } else {
447                                         childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
448                                                                                         null, parser, location);
449                                 }
450                                 return childBuilder;
451                         }
452
453                         childType = GetChildControlType (tagid, atts);
454                         if (childType == null)
455                                 return null;
456
457                         childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
458                                                               location.BeginLine, location.Filename);
459
460                         return childBuilder;
461                 }
462
463                 internal virtual object CreateInstance ()
464                 {
465                         // HtmlGenericControl, HtmlTableCell...
466                         object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
467                         object [] args = null;
468                         if (atts != null && atts.Length > 0) {
469                                 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
470                                 if (att.NeedsTag)
471                                         args = new object [] {tagName};
472                         }
473
474                         return Activator.CreateInstance (type, args);
475                 }
476
477                 internal virtual void CreateChildren (object parent) 
478                 {
479                         if (children == null || children.Count == 0)
480                                 return;
481
482                         IParserAccessor parser = parent as IParserAccessor;
483                         if (parser == null)
484                                 return;
485
486                         foreach (object o in children) {
487                                 if (o is string) {
488                                         parser.AddParsedSubObject (new LiteralControl ((string) o));
489                                 } else {
490                                         parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
491                                 }
492                         }
493                 }
494 #if NET_2_0
495                 [MonoTODO ("unsure, lack documentation")]
496                 public virtual object BuildObject ()
497                 {
498                         return CreateInstance ();
499                 }
500
501                 internal void ResetState()
502                 {
503                         haveParserVariable = false;
504
505                         if (Children != null) {
506                                 foreach (object child in Children) {
507                                         ControlBuilder cb = child as ControlBuilder;
508                                         if (cb != null)
509                                                 cb.ResetState ();
510                                 }
511                         }
512                 }
513 #endif
514         }
515 }