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