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