b3d4ef1b6c07735148204ae831db020afbeeab19
[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 CodeStatementCollection flushOutputStatements;
67                 internal CodeMemberMethod renderMethod;
68                 internal int renderIndex;
69                 internal bool isProperty;
70                 internal ILocation location;
71                 ArrayList otherTags;
72
73                 public ControlBuilder ()
74                 {
75                 }
76
77                 internal ControlBuilder (TemplateParser parser,
78                                          ControlBuilder parentBuilder,
79                                          Type type,
80                                          string tagName,
81                                          string id,
82                                          IDictionary attribs,
83                                          int line,
84                                          string sourceFileName)
85
86                 {
87                         this.parser = parser;
88                         this.parentBuilder = parentBuilder;
89                         this.type = type;
90                         this.tagName = tagName;
91                         this.id = id;
92                         this.attribs = attribs;
93                         this.line = line;
94                         this.fileName = sourceFileName;
95                 }
96
97                 internal void EnsureOtherTags ()
98                 {
99                         if (otherTags == null)
100                                 otherTags = new ArrayList ();
101                 }
102                 
103                 internal ArrayList OtherTags {
104                         get { return otherTags; }
105                 }
106
107                 public Type ControlType {
108                         get { return type; }
109                 }
110
111                 protected bool FChildrenAsProperties {
112                         get { return childrenAsProperties; }
113                 }
114
115                 protected bool FIsNonParserAccessor {
116                         get { return !isIParserAccessor; }
117                 }
118
119                 public bool HasAspCode {
120                         get { return hasAspCode; }
121                 }
122
123                 public string ID {
124                         get { return id; }
125                         set { id = value; }
126                 }
127
128                 internal ArrayList Children {
129                         get { return children; }
130                 }
131
132                 internal void SetControlType (Type t)
133                 {
134                         type = t;
135                 }
136                 
137                 protected bool InDesigner {
138                         get { return false; }
139                 }
140
141                 public Type NamingContainerType {
142                         get {
143                                 if (parentBuilder == null)
144                                         return typeof (Control);
145
146                                 Type ptype = parentBuilder.ControlType;
147                                 if (ptype == null)
148                                         return parentBuilder.NamingContainerType;
149
150                                 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
151                                         return parentBuilder.NamingContainerType;
152
153                                 return ptype;
154                         }
155                 }
156
157 #if NET_2_0
158                 public
159 #else
160                 internal
161 #endif
162                 Type BindingContainerType {
163                         get {
164                                 if (parentBuilder == null)
165                                         return typeof (Control);
166                                         
167                                 if (parentBuilder is TemplateBuilder && ((TemplateBuilder)parentBuilder).ContainerType != null)
168                                         return ((TemplateBuilder)parentBuilder).ContainerType;
169
170                                 Type ptype = parentBuilder.ControlType;
171                                 if (ptype == null)
172                                         return parentBuilder.BindingContainerType;
173
174                                 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
175                                         return parentBuilder.BindingContainerType;
176
177                                 return ptype;
178                         }
179                 }
180
181                 internal TemplateBuilder ParentTemplateBuilder {
182                         get {
183                                 if (parentBuilder == null)
184                                         return null;
185                                 else if (parentBuilder is TemplateBuilder)
186                                         return (TemplateBuilder) parentBuilder;
187                                 else
188                                         return parentBuilder.ParentTemplateBuilder;
189                         }
190                 }
191
192                 protected TemplateParser Parser {
193                         get { return parser; }
194                 }
195
196                 public string TagName {
197                         get { return tagName; }
198                 }
199
200                 internal RootBuilder Root {
201                         get {
202                                 if (GetType () == typeof (RootBuilder))
203                                         return (RootBuilder) this;
204
205                                 return (RootBuilder) parentBuilder.Root;
206                         }
207                 }
208
209                 internal bool ChildrenAsProperties {
210                         get { return childrenAsProperties; }
211                 }
212                 
213                 public virtual bool AllowWhitespaceLiterals ()
214                 {
215                         return true;
216                 }
217
218                 public virtual void AppendLiteralString (string s)
219                 {
220                         if (s == null || s == "")
221                                 return;
222
223                         if (childrenAsProperties || !isIParserAccessor) {
224                                 if (defaultPropertyBuilder != null) {
225                                         defaultPropertyBuilder.AppendLiteralString (s);
226                                 } else if (s.Trim () != "") {
227                                         throw new HttpException ("Literal content not allowed for " + tagName + " " +
228                                                                 GetType () + " \"" + s + "\"");
229                                 }
230
231                                 return;
232                         }
233                         
234                         if (!AllowWhitespaceLiterals () && s.Trim () == "")
235                                 return;
236
237                         if (HtmlDecodeLiterals ())
238                                 s = HttpUtility.HtmlDecode (s);
239
240                         if (children == null)
241                                 children = new ArrayList ();
242
243                         children.Add (s);
244                 }
245
246                 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
247                 {
248                         subBuilder.OnAppendToParentBuilder (this);
249                         
250                         subBuilder.parentBuilder = this;
251                         if (childrenAsProperties) {
252                                 AppendToProperty (subBuilder);
253                                 return;
254                         }
255
256                         if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
257                                 AppendCode (subBuilder);
258                                 return;
259                         }
260
261                         if (children == null)
262                                 children = new ArrayList ();
263
264                         children.Add (subBuilder);
265                 }
266
267                 void AppendToProperty (ControlBuilder subBuilder)
268                 {
269                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
270                                 throw new HttpException ("Code render not supported here.");
271
272                         if (defaultPropertyBuilder != null) {
273                                 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
274                                 return;
275                         }
276
277                         if (children == null)
278                                 children = new ArrayList ();
279
280                         children.Add (subBuilder);
281                 }
282
283                 void AppendCode (ControlBuilder subBuilder)
284                 {
285                         if (type != null && !(typeof (Control).IsAssignableFrom (type)))
286                                 throw new HttpException ("Code render not supported here.");
287
288                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
289                                 hasAspCode = true;
290
291                         if (children == null)
292                                 children = new ArrayList ();
293
294                         children.Add (subBuilder);
295                 }
296
297                 public virtual void CloseControl ()
298                 {
299                 }
300
301                 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
302                                                                     ControlBuilder parentBuilder,
303                                                                     Type type,
304                                                                     string tagName,
305                                                                     string id,
306                                                                     IDictionary attribs,
307                                                                     int line,
308                                                                     string sourceFileName)
309                 {
310                         ControlBuilder  builder;
311                         object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
312                         if (atts != null && atts.Length > 0) {
313                                 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
314                                 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
315                         } else {
316                                 builder = new ControlBuilder ();
317                         }
318
319                         builder.Init (parser, parentBuilder, type, tagName, id, attribs);
320                         builder.line = line;
321                         builder.fileName = sourceFileName;
322                         return builder;
323                 }
324
325                 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
326                 {
327                         return null;
328                 }
329
330                 public virtual bool HasBody ()
331                 {
332                         return true;
333                 }
334
335                 public virtual bool HtmlDecodeLiterals ()
336                 {
337                         return false;
338                 }
339
340                 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
341                 {
342                         PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
343                         if (prop == null) {
344                                 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
345                                 throw new HttpException (msg);
346                         }
347
348                         Type propType = prop.PropertyType;
349                         ControlBuilder builder = null;
350                         if (typeof (ICollection).IsAssignableFrom (propType)) {
351                                 builder = new CollectionBuilder ();
352                         } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
353                                 builder = new TemplateBuilder (prop);
354                         } else {
355                                 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
356                                                                  null, atts, line, fileName);
357                                 builder.isProperty = true;
358                                 return builder;
359                         }
360
361                         builder.Init (parser, this, null, prop.Name, null, atts);
362                         builder.fileName = fileName;
363                         builder.line = line;
364                         builder.isProperty = true;
365                         return builder;
366                 }
367                 
368                 public virtual void Init (TemplateParser parser,
369                                           ControlBuilder parentBuilder,
370                                           Type type,
371                                           string tagName,
372                                           string id,
373                                           IDictionary attribs)
374                 {
375                         this.parser = parser;
376                         if (parser != null)
377                                 this.location = parser.Location;
378
379                         this.parentBuilder = parentBuilder;
380                         this.type = type;
381                         this.tagName = tagName;
382                         this.id = id;
383                         this.attribs = attribs;
384                         if (type == null)
385                                 return;
386
387                         if (this is TemplateBuilder)
388                                 return;
389
390                         object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
391                         
392                         if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
393                                 isIParserAccessor = false;
394                                 childrenAsProperties = true;
395                         } else if (atts.Length > 0) {
396                                 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
397                                 childrenAsProperties = att.ChildrenAsProperties;
398                                 if (childrenAsProperties && att.DefaultProperty != "") {
399                                         defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
400                                                                                         parser, null);
401                                 }
402                         }
403                 }
404
405                 public virtual bool NeedsTagInnerText ()
406                 {
407                         return false;
408                 }
409
410                 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
411                 {
412                         if (defaultPropertyBuilder == null)
413                                 return;
414
415                         ControlBuilder old = defaultPropertyBuilder;
416                         defaultPropertyBuilder = null;
417                         AppendSubBuilder (old);
418                 }
419
420                 internal void SetTagName (string name)
421                 {
422                         tagName = name;
423                 }
424                 
425                 public virtual void SetTagInnerText (string text)
426                 {
427                 }
428
429                 internal string GetNextID (string proposedID)
430                 {
431                         if (proposedID != null && proposedID.Trim () != "")
432                                 return proposedID;
433
434                         return "_bctrl_" + nextID++;
435                 }
436
437                 internal virtual ControlBuilder CreateSubBuilder (string tagid,
438                                                                   Hashtable atts,
439                                                                   Type childType,
440                                                                   TemplateParser parser,
441                                                                   ILocation location)
442                 {
443                         ControlBuilder childBuilder = null;
444                         if (childrenAsProperties) {
445                                 if (defaultPropertyBuilder == null) {
446                                         childBuilder = CreatePropertyBuilder (tagid, parser, atts);
447                                 } else {
448                                         childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
449                                                                                         null, parser, location);
450                                 }
451                                 return childBuilder;
452                         }
453
454                         childType = GetChildControlType (tagid, atts);
455                         if (childType == null)
456                                 return null;
457
458                         childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
459                                                               location.BeginLine, location.Filename);
460
461                         return childBuilder;
462                 }
463
464                 internal virtual object CreateInstance ()
465                 {
466                         // HtmlGenericControl, HtmlTableCell...
467                         object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
468                         object [] args = null;
469                         if (atts != null && atts.Length > 0) {
470                                 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
471                                 if (att.NeedsTag)
472                                         args = new object [] {tagName};
473                         }
474
475                         return Activator.CreateInstance (type, args);
476                 }
477
478                 internal virtual void CreateChildren (object parent) 
479                 {
480                         if (children == null || children.Count == 0)
481                                 return;
482
483                         IParserAccessor parser = parent as IParserAccessor;
484                         if (parser == null)
485                                 return;
486
487                         foreach (object o in children) {
488                                 if (o is string) {
489                                         parser.AddParsedSubObject (new LiteralControl ((string) o));
490                                 } else {
491                                         parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
492                                 }
493                         }
494                 }
495 #if NET_2_0
496                 [MonoTODO ("unsure, lack documentation")]
497                 public virtual object BuildObject ()
498                 {
499                         return CreateInstance ();
500                 }
501
502                 internal void ResetState()
503                 {
504                         haveParserVariable = false;
505
506                         if (Children != null) {
507                                 foreach (object child in Children) {
508                                         ControlBuilder cb = child as ControlBuilder;
509                                         if (cb != null)
510                                                 cb.ResetState ();
511                                 }
512                         }
513                 }
514 #endif
515         }
516 }