2008-05-28 Marek Habersack <mhabersack@novell.com>
[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.Configuration;
33 using System.CodeDom;
34 using System.Reflection;
35 using System.Security.Permissions;
36 using System.Web.Compilation;
37 using System.Web.Configuration;
38 using System.IO;
39 using System.Web.UI.WebControls;
40
41 namespace System.Web.UI {
42
43         // CAS
44         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
45         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
46         public class ControlBuilder
47         {
48                 internal static BindingFlags flagsNoCase = BindingFlags.Public |
49                         BindingFlags.Instance |
50                         BindingFlags.Static |
51                         BindingFlags.IgnoreCase;
52
53                 ControlBuilder myNamingContainer;
54                 TemplateParser parser;
55                 internal ControlBuilder parentBuilder;
56                 Type type;             
57                 string tagName;
58                 string id;
59                 internal IDictionary attribs;
60                 internal int line;
61                 internal string fileName;
62                 bool childrenAsProperties;
63                 bool isIParserAccessor = true;
64                 bool hasAspCode;
65                 internal ControlBuilder defaultPropertyBuilder;
66                 ArrayList children;
67                 static int nextID;
68
69                 internal bool haveParserVariable;
70                 internal CodeMemberMethod method;
71                 internal CodeStatementCollection methodStatements;
72                 internal CodeMemberMethod renderMethod;
73                 internal int renderIndex;
74                 internal bool isProperty;
75                 internal ILocation location;
76                 ArrayList otherTags;
77 #if NET_2_0
78                 static string privateBinPath;
79 #endif
80                 
81                 public ControlBuilder ()
82                 {
83                 }
84
85                 internal ControlBuilder (TemplateParser parser,
86                                          ControlBuilder parentBuilder,
87                                          Type type,
88                                          string tagName,
89                                          string id,
90                                          IDictionary attribs,
91                                          int line,
92                                          string sourceFileName)
93
94                 {
95                         this.parser = parser;
96                         this.parentBuilder = parentBuilder;
97                         this.type = type;
98                         this.tagName = tagName;
99                         this.id = id;
100                         this.attribs = attribs;
101                         this.line = line;
102                         this.fileName = sourceFileName;
103                 }
104
105                 internal void EnsureOtherTags ()
106                 {
107                         if (otherTags == null)
108                                 otherTags = new ArrayList ();
109                 }
110                 
111                 internal ArrayList OtherTags {
112                         get { return otherTags; }
113                 }
114
115                 public Type ControlType {
116                         get { return type; }
117                 }
118
119                 protected bool FChildrenAsProperties {
120                         get { return childrenAsProperties; }
121                 }
122
123                 protected bool FIsNonParserAccessor {
124                         get { return !isIParserAccessor; }
125                 }
126
127                 public bool HasAspCode {
128                         get { return hasAspCode; }
129                 }
130
131                 public string ID {
132                         get { return id; }
133                         set { id = value; }
134                 }
135
136                 internal ArrayList Children {
137                         get { return children; }
138                 }
139
140                 internal void SetControlType (Type t)
141                 {
142                         type = t;
143                 }
144                 
145                 protected bool InDesigner {
146                         get { return false; }
147                 }
148
149                 public Type NamingContainerType {
150                         get {
151                                 ControlBuilder cb = myNamingContainer;
152                                 
153                                 if (cb == null)
154                                         return typeof (Control);
155
156                                 return cb.ControlType;
157                         }
158                 }
159
160                 ControlBuilder MyNamingContainer {
161                         get {
162                                 if (myNamingContainer == null) {
163                                         Type controlType = parentBuilder != null ? parentBuilder.ControlType : null;
164                                         
165                                         if (parentBuilder == null && controlType == null)
166                                                 myNamingContainer = null;
167                                         else if (parentBuilder is TemplateBuilder)
168                                                 myNamingContainer = parentBuilder;
169                                         else if (controlType != null && typeof (INamingContainer).IsAssignableFrom (controlType))
170                                                 myNamingContainer = parentBuilder;
171                                         else
172                                                 myNamingContainer = parentBuilder.MyNamingContainer;
173                                 }
174
175                                 return myNamingContainer;
176                         }
177                 }
178                         
179 #if NET_2_0
180                 public virtual
181 #else
182                 internal
183 #endif
184                 Type BindingContainerType {
185                         get {
186                                 ControlBuilder cb = MyNamingContainer;
187                                 if (cb == null)
188                                         return typeof (Control);
189
190 #if NET_2_0
191                                 if (cb is ContentBuilderInternal)
192                                         return cb.BindingContainerType;
193 #endif
194
195                                 if (cb is TemplateBuilder) {
196                                         Type ct =((TemplateBuilder) cb).ContainerType;
197                                         if (ct == null)
198                                                 return typeof (Control);
199                                         return ct;
200                                 }
201
202                                 return cb.ControlType;
203                         }
204                 }
205
206                 internal TemplateBuilder ParentTemplateBuilder {
207                         get {
208                                 if (parentBuilder == null)
209                                         return null;
210                                 else if (parentBuilder is TemplateBuilder)
211                                         return (TemplateBuilder) parentBuilder;
212                                 else
213                                         return parentBuilder.ParentTemplateBuilder;
214                         }
215                 }
216
217                 protected TemplateParser Parser {
218                         get { return parser; }
219                 }
220
221                 public string TagName {
222                         get { return tagName; }
223                 }
224
225                 internal RootBuilder Root {
226                         get {
227                                 if (GetType () == typeof (RootBuilder))
228                                         return (RootBuilder) this;
229
230                                 return (RootBuilder) parentBuilder.Root;
231                         }
232                 }
233
234                 internal bool ChildrenAsProperties {
235                         get { return childrenAsProperties; }
236                 }
237                 
238                 public virtual bool AllowWhitespaceLiterals ()
239                 {
240                         return true;
241                 }
242
243                 public virtual void AppendLiteralString (string s)
244                 {
245                         if (s == null || s.Length == 0)
246                                 return;
247
248                         if (childrenAsProperties || !isIParserAccessor) {
249                                 if (defaultPropertyBuilder != null) {
250                                         defaultPropertyBuilder.AppendLiteralString (s);
251                                 } else if (s.Trim ().Length != 0) {
252                                         throw new HttpException (String.Format ("Literal content not allowed for '{0}' {1} \"{2}\"",
253                                                                                 tagName, GetType (), s));
254                                 }
255
256                                 return;
257                         }
258                         
259                         if (!AllowWhitespaceLiterals () && s.Trim ().Length == 0)
260                                 return;
261
262                         if (HtmlDecodeLiterals ())
263                                 s = HttpUtility.HtmlDecode (s);
264
265                         if (children == null)
266                                 children = new ArrayList ();
267
268                         children.Add (s);
269                 }
270
271                 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
272                 {
273                         subBuilder.OnAppendToParentBuilder (this);
274                         
275                         subBuilder.parentBuilder = this;
276                         if (childrenAsProperties) {
277                                 AppendToProperty (subBuilder);
278                                 return;
279                         }
280
281                         if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
282                                 AppendCode (subBuilder);
283                                 return;
284                         }
285
286                         if (children == null)
287                                 children = new ArrayList ();
288
289                         children.Add (subBuilder);
290                 }
291
292                 void AppendToProperty (ControlBuilder subBuilder)
293                 {
294                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
295                                 throw new HttpException ("Code render not supported here.");
296
297                         if (defaultPropertyBuilder != null) {
298                                 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
299                                 return;
300                         }
301
302                         if (children == null)
303                                 children = new ArrayList ();
304
305                         children.Add (subBuilder);
306                 }
307
308                 void AppendCode (ControlBuilder subBuilder)
309                 {
310                         if (type != null && !(typeof (Control).IsAssignableFrom (type)))
311                                 throw new HttpException ("Code render not supported here.");
312
313                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
314                                 hasAspCode = true;
315
316                         if (children == null)
317                                 children = new ArrayList ();
318
319                         children.Add (subBuilder);
320                 }
321
322                 public virtual void CloseControl ()
323                 {
324                 }
325
326 #if NET_2_0             
327                 static Type MapTagType (Type tagType)
328                 {
329                         if (tagType == null)
330                                 return null;
331                         
332                         PagesSection ps = WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
333                         if (ps == null)
334                                 return tagType;
335
336                         TagMapCollection tags = ps.TagMapping;
337                         if (tags == null || tags.Count == 0)
338                                 return tagType;
339                         
340                         string tagTypeName = tagType.ToString ();
341                         Type mappedType, originalType;
342                         string originalTypeName = String.Empty, mappedTypeName = String.Empty;
343                         bool missingType;
344                         Exception error;
345                         
346                         foreach (TagMapInfo tmi in tags) {
347                                 error = null;
348                                 originalType = null;
349                                 
350                                 try {
351                                         originalTypeName = tmi.TagType;
352                                         originalType = HttpApplication.LoadType (originalTypeName);
353                                         if (originalType == null)
354                                                 missingType = true;
355                                         else
356                                                 missingType = false;
357                                 } catch (Exception ex) {
358                                         missingType = true;
359                                         error = ex;
360                                 }
361                                 if (missingType)
362                                         throw new HttpException (String.Format ("Could not load type {0}", originalTypeName), error);
363                                 
364                                 if (originalTypeName == tagTypeName) {
365                                         mappedTypeName = tmi.MappedTagType;
366                                         error = null;
367                                         mappedType = null;
368                                         
369                                         try {
370                                                 mappedType = HttpApplication.LoadType (mappedTypeName);
371                                                 if (mappedType == null)
372                                                         missingType = true;
373                                                 else
374                                                         missingType = false;
375                                         } catch (Exception ex) {
376                                                 missingType = true;
377                                                 error = ex;
378                                         }
379
380                                         if (missingType)
381                                                 throw new HttpException (String.Format ("Could not load type {0}", mappedTypeName),
382                                                                          error);
383                                         
384                                         if (!mappedType.IsSubclassOf (originalType))
385                                                 throw new ConfigurationErrorsException (
386                                                         String.Format ("The specified type '{0}' used for mapping must inherit from the original type '{1}'.", mappedTypeName, originalTypeName));
387
388                                         return mappedType;
389                                 }
390                         }
391                         
392                         return tagType;
393                 }
394 #endif
395
396                 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
397                                                                     ControlBuilder parentBuilder,
398                                                                     Type type,
399                                                                     string tagName,
400                                                                     string id,
401                                                                     IDictionary attribs,
402                                                                     int line,
403                                                                     string sourceFileName)
404                 {
405
406                         Type tagType;
407 #if NET_2_0
408                         tagType = MapTagType (type);
409 #else
410                         tagType = type;
411 #endif
412                         ControlBuilder  builder;
413                         object [] atts = tagType.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
414                         if (atts != null && atts.Length > 0) {
415                                 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
416                                 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
417                         } else {
418                                 builder = new ControlBuilder ();
419                         }
420
421                         builder.Init (parser, parentBuilder, tagType, tagName, id, attribs);
422                         builder.line = line;
423                         builder.fileName = sourceFileName;
424                         return builder;
425                 }
426
427                 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
428                 {
429                         return null;
430                 }
431
432                 public virtual bool HasBody ()
433                 {
434                         return true;
435                 }
436
437                 public virtual bool HtmlDecodeLiterals ()
438                 {
439                         return false;
440                 }
441
442                 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
443                 {
444                         PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
445                         if (prop == null) {
446                                 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
447                                 throw new HttpException (msg);
448                         }
449
450                         Type propType = prop.PropertyType;
451                         ControlBuilder builder = null;
452                         if (typeof (ICollection).IsAssignableFrom (propType)) {
453                                 builder = new CollectionBuilder ();
454                         } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
455                                 builder = new TemplateBuilder (prop);
456                         } else if (typeof (string) == propType) {
457                                 builder = new StringPropertyBuilder (prop.Name);
458                         } else {
459                                 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
460                                                                  null, atts, line, fileName);
461                                 builder.isProperty = true;
462                                 return builder;
463                         }
464
465                         builder.Init (parser, this, null, prop.Name, null, atts);
466                         builder.fileName = fileName;
467                         builder.line = line;
468                         builder.isProperty = true;
469                         return builder;
470                 }
471                 
472                 public virtual void Init (TemplateParser parser,
473                                           ControlBuilder parentBuilder,
474                                           Type type,
475                                           string tagName,
476                                           string id,
477                                           IDictionary attribs)
478                 {
479                         this.parser = parser;
480                         if (parser != null)
481                                 this.location = parser.Location;
482
483                         this.parentBuilder = parentBuilder;
484                         this.type = type;
485                         this.tagName = tagName;
486                         this.id = id;
487                         this.attribs = attribs;
488                         if (type == null)
489                                 return;
490
491                         if (this is TemplateBuilder)
492                                 return;
493
494                         object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
495                         
496                         if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
497                                 isIParserAccessor = false;
498                                 childrenAsProperties = true;
499                         } else if (atts.Length > 0) {
500                                 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
501                                 childrenAsProperties = att.ChildrenAsProperties;
502                                 if (childrenAsProperties && att.DefaultProperty.Length != 0)
503                                         defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
504                                                                                         parser, null);
505                         }
506                 }
507
508                 public virtual bool NeedsTagInnerText ()
509                 {
510                         return false;
511                 }
512
513                 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
514                 {
515                         if (defaultPropertyBuilder == null)
516                                 return;
517
518                         ControlBuilder old = defaultPropertyBuilder;
519                         defaultPropertyBuilder = null;
520                         AppendSubBuilder (old);
521                 }
522
523                 internal void SetTagName (string name)
524                 {
525                         tagName = name;
526                 }
527                 
528                 public virtual void SetTagInnerText (string text)
529                 {
530                 }
531
532                 internal string GetNextID (string proposedID)
533                 {
534                         if (proposedID != null && proposedID.Trim ().Length != 0)
535                                 return proposedID;
536
537                         return "_bctrl_" + nextID++;
538                 }
539
540                 internal virtual ControlBuilder CreateSubBuilder (string tagid,
541                                                                   Hashtable atts,
542                                                                   Type childType,
543                                                                   TemplateParser parser,
544                                                                   ILocation location)
545                 {
546                         ControlBuilder childBuilder = null;
547                         if (childrenAsProperties) {
548                                 if (defaultPropertyBuilder == null)
549                                         childBuilder = CreatePropertyBuilder (tagid, parser, atts);
550                                 else {
551                                         if (defaultPropertyBuilder.TagName == tagid) {
552                                                 // The child tag is the same what our default property name. Act as if there was
553                                                 // no default property builder, or otherwise we'll end up with invalid nested
554                                                 // builder call.
555                                                 defaultPropertyBuilder = null;
556                                                 childBuilder = CreatePropertyBuilder (tagid, parser, atts);
557                                         } else
558                                                 childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
559                                                                                                         null, parser,
560                                                                                                         location);
561                                 }
562                                 return childBuilder;
563                         }
564
565                         if (tagName == tagid)
566                                 return null;
567                         
568                         childType = GetChildControlType (tagid, atts);
569                         if (childType == null)
570                                 return null;
571
572                         childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
573                                                               location.BeginLine, location.Filename);
574
575                         return childBuilder;
576                 }
577
578                 internal virtual object CreateInstance ()
579                 {
580                         // HtmlGenericControl, HtmlTableCell...
581                         object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
582                         object [] args = null;
583                         if (atts != null && atts.Length > 0) {
584                                 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
585                                 if (att.NeedsTag)
586                                         args = new object [] {tagName};
587                         }
588
589                         return Activator.CreateInstance (type, args);
590                 }
591
592                 internal virtual void CreateChildren (object parent) 
593                 {
594                         if (children == null || children.Count == 0)
595                                 return;
596
597                         IParserAccessor parser = parent as IParserAccessor;
598                         if (parser == null)
599                                 return;
600
601                         foreach (object o in children) {
602                                 if (o is string) {
603                                         parser.AddParsedSubObject (new LiteralControl ((string) o));
604                                 } else {
605                                         parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
606                                 }
607                         }
608                 }
609 #if NET_2_0
610                 [MonoTODO ("unsure, lack documentation")]
611                 public virtual object BuildObject ()
612                 {
613                         return CreateInstance ();
614                 }
615
616                 internal void ResetState()
617                 {
618                         renderIndex = 0;
619                         haveParserVariable = false;
620
621                         if (Children != null) {
622                                 foreach (object child in Children) {
623                                         ControlBuilder cb = child as ControlBuilder;
624                                         if (cb != null)
625                                                 cb.ResetState ();
626                                 }
627                         }
628                 }
629 #endif
630         }
631 }