2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[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                 protected TemplateParser Parser {
156                         get { return parser; }
157                 }
158
159                 public string TagName {
160                         get { return tagName; }
161                 }
162
163                 internal RootBuilder Root {
164                         get {
165                                 if (GetType () == typeof (RootBuilder))
166                                         return (RootBuilder) this;
167
168                                 return (RootBuilder) parentBuilder.Root;
169                         }
170                 }
171
172                 internal bool ChildrenAsProperties {
173                         get { return childrenAsProperties; }
174                 }
175                 
176                 public virtual bool AllowWhitespaceLiterals ()
177                 {
178                         return true;
179                 }
180
181                 public virtual void AppendLiteralString (string s)
182                 {
183                         if (s == null || s == "")
184                                 return;
185
186                         if (childrenAsProperties || !isIParserAccessor) {
187                                 if (defaultPropertyBuilder != null) {
188                                         defaultPropertyBuilder.AppendLiteralString (s);
189                                 } else if (s.Trim () != "") {
190                                         throw new HttpException ("Literal content not allowed for " + tagName + " " +
191                                                                 GetType () + " \"" + s + "\"");
192                                 }
193
194                                 return;
195                         }
196                         
197                         if (!AllowWhitespaceLiterals () && s.Trim () == "")
198                                 return;
199
200                         if (HtmlDecodeLiterals ())
201                                 s = HttpUtility.HtmlDecode (s);
202
203                         if (children == null)
204                                 children = new ArrayList ();
205
206                         children.Add (s);
207                 }
208
209                 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
210                 {
211                         subBuilder.OnAppendToParentBuilder (this);
212                         
213                         subBuilder.parentBuilder = this;
214                         if (childrenAsProperties) {
215                                 AppendToProperty (subBuilder);
216                                 return;
217                         }
218
219                         if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
220                                 AppendCode (subBuilder);
221                                 return;
222                         }
223
224                         if (children == null)
225                                 children = new ArrayList ();
226
227                         children.Add (subBuilder);
228                 }
229
230                 void AppendToProperty (ControlBuilder subBuilder)
231                 {
232                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
233                                 throw new HttpException ("Code render not supported here.");
234
235                         if (defaultPropertyBuilder != null) {
236                                 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
237                                 return;
238                         }
239
240                         if (children == null)
241                                 children = new ArrayList ();
242
243                         children.Add (subBuilder);
244                 }
245
246                 void AppendCode (ControlBuilder subBuilder)
247                 {
248                         if (type != null && !(typeof (Control).IsAssignableFrom (type)))
249                                 throw new HttpException ("Code render not supported here.");
250
251                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
252                                 hasAspCode = true;
253
254                         if (children == null)
255                                 children = new ArrayList ();
256
257                         children.Add (subBuilder);
258                 }
259
260                 public virtual void CloseControl ()
261                 {
262                 }
263
264                 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
265                                                                     ControlBuilder parentBuilder,
266                                                                     Type type,
267                                                                     string tagName,
268                                                                     string id,
269                                                                     IDictionary attribs,
270                                                                     int line,
271                                                                     string sourceFileName)
272                 {
273                         ControlBuilder  builder;
274                         object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
275                         if (atts != null && atts.Length > 0) {
276                                 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
277                                 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
278                         } else {
279                                 builder = new ControlBuilder ();
280                         }
281
282                         builder.Init (parser, parentBuilder, type, tagName, id, attribs);
283                         builder.line = line;
284                         builder.fileName = sourceFileName;
285                         return builder;
286                 }
287
288                 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
289                 {
290                         return null;
291                 }
292
293                 public virtual bool HasBody ()
294                 {
295                         return true;
296                 }
297
298                 public virtual bool HtmlDecodeLiterals ()
299                 {
300                         return false;
301                 }
302
303                 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
304                 {
305                         PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
306                         if (prop == null) {
307                                 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
308                                 throw new HttpException (msg);
309                         }
310
311                         Type propType = prop.PropertyType;
312                         ControlBuilder builder = null;
313                         if (typeof (ICollection).IsAssignableFrom (propType)) {
314                                 builder = new CollectionBuilder ();
315                         } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
316                                 builder = new TemplateBuilder (prop);
317                         } else {
318                                 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
319                                                                  null, atts, line, fileName);
320                                 builder.isProperty = true;
321                                 return builder;
322                         }
323
324                         builder.Init (parser, this, null, prop.Name, null, atts);
325                         builder.fileName = fileName;
326                         builder.line = line;
327                         builder.isProperty = true;
328                         return builder;
329                 }
330                 
331                 public virtual void Init (TemplateParser parser,
332                                           ControlBuilder parentBuilder,
333                                           Type type,
334                                           string tagName,
335                                           string id,
336                                           IDictionary attribs)
337                 {
338                         this.parser = parser;
339                         if (parser != null)
340                                 this.location = parser.Location;
341
342                         this.parentBuilder = parentBuilder;
343                         this.type = type;
344                         this.tagName = tagName;
345                         this.id = id;
346                         this.attribs = attribs;
347                         if (type == null)
348                                 return;
349
350                         if (this is TemplateBuilder)
351                                 return;
352
353                         object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
354                         
355                         if (!typeof (IParserAccessor).IsAssignableFrom (type) && atts.Length == 0) {
356                                 isIParserAccessor = false;
357                                 childrenAsProperties = true;
358                         } else if (atts.Length > 0) {
359                                 ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
360                                 childrenAsProperties = att.ChildrenAsProperties;
361                                 if (childrenAsProperties && att.DefaultProperty != "") {
362                                         defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
363                                                                                         parser, null);
364                                 }
365                         }
366                 }
367
368                 public virtual bool NeedsTagInnerText ()
369                 {
370                         return false;
371                 }
372
373                 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
374                 {
375                         if (defaultPropertyBuilder == null)
376                                 return;
377
378                         ControlBuilder old = defaultPropertyBuilder;
379                         defaultPropertyBuilder = null;
380                         AppendSubBuilder (old);
381                 }
382
383                 internal void SetTagName (string name)
384                 {
385                         tagName = name;
386                 }
387                 
388                 public virtual void SetTagInnerText (string text)
389                 {
390                 }
391
392                 internal string GetNextID (string proposedID)
393                 {
394                         if (proposedID != null && proposedID.Trim () != "")
395                                 return proposedID;
396
397                         return "_bctrl_" + nextID++;
398                 }
399
400                 internal virtual ControlBuilder CreateSubBuilder (string tagid,
401                                                                   Hashtable atts,
402                                                                   Type childType,
403                                                                   TemplateParser parser,
404                                                                   ILocation location)
405                 {
406                         ControlBuilder childBuilder = null;
407                         if (childrenAsProperties) {
408                                 if (defaultPropertyBuilder == null) {
409                                         childBuilder = CreatePropertyBuilder (tagid, parser, atts);
410                                 } else {
411                                         childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
412                                                                                         null, parser, location);
413                                 }
414                                 return childBuilder;
415                         }
416
417                         childType = GetChildControlType (tagid, atts);
418                         if (childType == null)
419                                 return null;
420
421                         childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
422                                                               location.BeginLine, location.Filename);
423
424                         return childBuilder;
425                 }
426
427                 internal virtual object CreateInstance ()
428                 {
429                         // HtmlGenericControl, HtmlTableCell...
430                         object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
431                         object [] args = null;
432                         if (atts != null && atts.Length > 0) {
433                                 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
434                                 if (att.NeedsTag)
435                                         args = new object [] {tagName};
436                         }
437
438                         return Activator.CreateInstance (type, args);
439                 }
440
441                 internal virtual void CreateChildren (object parent) 
442                 {
443                         if (children == null || children.Count == 0)
444                                 return;
445
446                         IParserAccessor parser = parent as IParserAccessor;
447                         if (parser == null)
448                                 return;
449
450                         foreach (object o in children) {
451                                 if (o is string) {
452                                         parser.AddParsedSubObject (new LiteralControl ((string) o));
453                                 } else {
454                                         parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
455                                 }
456                         }
457                 }
458         }
459 }
460