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