Add licensing info
[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
70                 public ControlBuilder ()
71                 {
72                 }
73
74                 internal ControlBuilder (TemplateParser parser,
75                                          ControlBuilder parentBuilder,
76                                          Type type,
77                                          string tagName,
78                                          string id,
79                                          IDictionary attribs,
80                                          int line,
81                                          string sourceFileName)
82
83                 {
84                         this.parser = parser;
85                         this.parentBuilder = parentBuilder;
86                         this.type = type;
87                         this.tagName = tagName;
88                         this.id = id;
89                         this.attribs = attribs;
90                         this.line = line;
91                         this.fileName = sourceFileName;
92                 }
93
94                 public Type ControlType {
95                         get { return type; }
96                 }
97
98                 protected bool FChildrenAsProperties {
99                         get { return childrenAsProperties; }
100                 }
101
102                 protected bool FIsNonParserAccessor {
103                         get { return !isIParserAccessor; }
104                 }
105
106                 public bool HasAspCode {
107                         get { return hasAspCode; }
108                 }
109
110                 public string ID {
111                         get { return id; }
112                         set { id = value; }
113                 }
114
115                 internal ArrayList Children {
116                         get { return children; }
117                 }
118
119                 internal void SetControlType (Type t)
120                 {
121                         type = t;
122                 }
123                 
124                 protected bool InDesigner {
125                         get { return false; }
126                 }
127
128                 public Type NamingContainerType {
129                         get {
130                                 if (parentBuilder == null)
131                                         return typeof (Control);
132
133                                 Type ptype = parentBuilder.ControlType;
134                                 if (ptype == null)
135                                         return parentBuilder.NamingContainerType;
136
137                                 if (!typeof (INamingContainer).IsAssignableFrom (ptype))
138                                         return parentBuilder.NamingContainerType;
139
140                                 return ptype;
141                         }
142                 }
143
144                 protected TemplateParser Parser {
145                         get { return parser; }
146                 }
147
148                 public string TagName {
149                         get { return tagName; }
150                 }
151
152                 internal RootBuilder Root {
153                         get {
154                                 if (GetType () == typeof (RootBuilder))
155                                         return (RootBuilder) this;
156
157                                 return (RootBuilder) parentBuilder.Root;
158                         }
159                 }
160
161                 internal bool ChildrenAsProperties {
162                         get { return childrenAsProperties; }
163                 }
164                 
165                 public virtual bool AllowWhitespaceLiterals ()
166                 {
167                         return true;
168                 }
169
170                 public virtual void AppendLiteralString (string s)
171                 {
172                         if (s == null || s == "")
173                                 return;
174
175                         if (childrenAsProperties || !isIParserAccessor) {
176                                 if (defaultPropertyBuilder != null) {
177                                         defaultPropertyBuilder.AppendLiteralString (s);
178                                 } else if (s.Trim () != "") {
179                                         throw new HttpException ("Literal content not allowed for " + tagName + " " +
180                                                                 GetType () + " \"" + s + "\"");
181                                 }
182
183                                 return;
184                         }
185                         
186                         if (!AllowWhitespaceLiterals () && s.Trim () == "")
187                                 return;
188
189                         if (HtmlDecodeLiterals ())
190                                 s = HttpUtility.HtmlDecode (s);
191
192                         if (children == null)
193                                 children = new ArrayList ();
194
195                         children.Add (s);
196                 }
197
198                 public virtual void AppendSubBuilder (ControlBuilder subBuilder)
199                 {
200                         subBuilder.OnAppendToParentBuilder (this);
201                         
202                         subBuilder.parentBuilder = this;
203                         if (childrenAsProperties) {
204                                 AppendToProperty (subBuilder);
205                                 return;
206                         }
207
208                         if (typeof (CodeRenderBuilder).IsAssignableFrom (subBuilder.GetType ())) {
209                                 AppendCode (subBuilder);
210                                 return;
211                         }
212
213                         if (children == null)
214                                 children = new ArrayList ();
215
216                         children.Add (subBuilder);
217                 }
218
219                 void AppendToProperty (ControlBuilder subBuilder)
220                 {
221                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
222                                 throw new HttpException ("Code render not supported here.");
223
224                         if (defaultPropertyBuilder != null) {
225                                 defaultPropertyBuilder.AppendSubBuilder (subBuilder);
226                                 return;
227                         }
228
229                         if (children == null)
230                                 children = new ArrayList ();
231
232                         children.Add (subBuilder);
233                 }
234
235                 void AppendCode (ControlBuilder subBuilder)
236                 {
237                         if (type != null && !(typeof (Control).IsAssignableFrom (type)))
238                                 throw new HttpException ("Code render not supported here.");
239
240                         if (typeof (CodeRenderBuilder) == subBuilder.GetType ())
241                                 hasAspCode = true;
242
243                         if (children == null)
244                                 children = new ArrayList ();
245
246                         children.Add (subBuilder);
247                 }
248
249                 public virtual void CloseControl ()
250                 {
251                 }
252
253                 public static ControlBuilder CreateBuilderFromType (TemplateParser parser,
254                                                                     ControlBuilder parentBuilder,
255                                                                     Type type,
256                                                                     string tagName,
257                                                                     string id,
258                                                                     IDictionary attribs,
259                                                                     int line,
260                                                                     string sourceFileName)
261                 {
262                         ControlBuilder  builder;
263                         object [] atts = type.GetCustomAttributes (typeof (ControlBuilderAttribute), true);
264                         if (atts != null && atts.Length > 0) {
265                                 ControlBuilderAttribute att = (ControlBuilderAttribute) atts [0];
266                                 builder = (ControlBuilder) Activator.CreateInstance (att.BuilderType);
267                         } else {
268                                 builder = new ControlBuilder ();
269                         }
270
271                         builder.Init (parser, parentBuilder, type, tagName, id, attribs);
272                         builder.line = line;
273                         builder.fileName = sourceFileName;
274                         return builder;
275                 }
276
277                 public virtual Type GetChildControlType (string tagName, IDictionary attribs)
278                 {
279                         return null;
280                 }
281
282                 public virtual bool HasBody ()
283                 {
284                         return true;
285                 }
286
287                 public virtual bool HtmlDecodeLiterals ()
288                 {
289                         return false;
290                 }
291
292                 ControlBuilder CreatePropertyBuilder (string propName, TemplateParser parser, IDictionary atts)
293                 {
294                         PropertyInfo prop = type.GetProperty (propName, flagsNoCase);
295                         if (prop == null) {
296                                 string msg = String.Format ("Property {0} not found in type {1}", propName, type);
297                                 throw new HttpException (msg);
298                         }
299
300                         Type propType = prop.PropertyType;
301                         ControlBuilder builder = null;
302                         if (typeof (ICollection).IsAssignableFrom (propType)) {
303                                 builder = new CollectionBuilder ();
304                         } else if (typeof (ITemplate).IsAssignableFrom (propType)) {
305                                 builder = new TemplateBuilder ();
306                         } else {
307                                 builder = CreateBuilderFromType (parser, parentBuilder, propType, prop.Name,
308                                                                  null, atts, line, fileName);
309                                 builder.isProperty = true;
310                                 return builder;
311                         }
312
313                         builder.Init (parser, this, null, prop.Name, null, atts);
314                         builder.fileName = fileName;
315                         builder.line = line;
316                         builder.isProperty = true;
317                         return builder;
318                 }
319                 
320                 public virtual void Init (TemplateParser parser,
321                                           ControlBuilder parentBuilder,
322                                           Type type,
323                                           string tagName,
324                                           string id,
325                                           IDictionary attribs)
326                 {
327                         this.parser = parser;
328                         if (parser != null)
329                                 this.location = parser.Location;
330
331                         this.parentBuilder = parentBuilder;
332                         this.type = type;
333                         this.tagName = tagName;
334                         this.id = id;
335                         this.attribs = attribs;
336                         if (type == null)
337                                 return;
338
339                         if (this is TemplateBuilder)
340                                 return;
341
342                         if (!typeof (IParserAccessor).IsAssignableFrom (type)) {
343                                 isIParserAccessor = false;
344                                 childrenAsProperties = true;
345                         } else {
346                                 object [] atts = type.GetCustomAttributes (typeof (ParseChildrenAttribute), true);
347                                 if (atts != null && atts.Length > 0) {
348                                         ParseChildrenAttribute att = (ParseChildrenAttribute) atts [0];
349                                         childrenAsProperties = att.ChildrenAsProperties;
350                                         if (childrenAsProperties && att.DefaultProperty != "") {
351                                                 defaultPropertyBuilder = CreatePropertyBuilder (att.DefaultProperty,
352                                                                                                 parser, null);
353                                         }
354                                 }
355                         }
356                 }
357
358                 public virtual bool NeedsTagInnerText ()
359                 {
360                         return false;
361                 }
362
363                 public virtual void OnAppendToParentBuilder (ControlBuilder parentBuilder)
364                 {
365                         if (defaultPropertyBuilder == null)
366                                 return;
367
368                         ControlBuilder old = defaultPropertyBuilder;
369                         defaultPropertyBuilder = null;
370                         AppendSubBuilder (old);
371                 }
372
373                 internal void SetTagName (string name)
374                 {
375                         tagName = name;
376                 }
377                 
378                 public virtual void SetTagInnerText (string text)
379                 {
380                 }
381
382                 internal string GetNextID (string proposedID)
383                 {
384                         if (proposedID != null && proposedID.Trim () != "")
385                                 return proposedID;
386
387                         return "_bctrl_" + nextID++;
388                 }
389
390                 internal virtual ControlBuilder CreateSubBuilder (string tagid,
391                                                                   Hashtable atts,
392                                                                   Type childType,
393                                                                   TemplateParser parser,
394                                                                   ILocation location)
395                 {
396                         ControlBuilder childBuilder = null;
397                         if (childrenAsProperties) {
398                                 if (defaultPropertyBuilder == null) {
399                                         childBuilder = CreatePropertyBuilder (tagid, parser, atts);
400                                 } else {
401                                         childBuilder = defaultPropertyBuilder.CreateSubBuilder (tagid, atts,
402                                                                                         null, parser, location);
403                                 }
404                                 return childBuilder;
405                         }
406
407                         childType = GetChildControlType (tagid, atts);
408                         if (childType == null)
409                                 return null;
410
411                         childBuilder = CreateBuilderFromType (parser, this, childType, tagid, id, atts,
412                                                               location.BeginLine, location.Filename);
413
414                         return childBuilder;
415                 }
416
417                 internal virtual object CreateInstance ()
418                 {
419                         // HtmlGenericControl, HtmlTableCell...
420                         object [] atts = type.GetCustomAttributes (typeof (ConstructorNeedsTagAttribute), true);
421                         object [] args = null;
422                         if (atts != null && atts.Length > 0) {
423                                 ConstructorNeedsTagAttribute att = (ConstructorNeedsTagAttribute) atts [0];
424                                 if (att.NeedsTag)
425                                         args = new object [] {tagName};
426                         }
427
428                         return Activator.CreateInstance (type, args);
429                 }
430
431                 internal virtual void CreateChildren (object parent) 
432                 {
433                         if (children == null || children.Count == 0)
434                                 return;
435
436                         IParserAccessor parser = parent as IParserAccessor;
437                         if (parser == null)
438                                 return;
439
440                         foreach (object o in children) {
441                                 if (o is string) {
442                                         parser.AddParsedSubObject (new LiteralControl ((string) o));
443                                 } else {
444                                         parser.AddParsedSubObject (((ControlBuilder) o).CreateInstance ());
445                                 }
446                         }
447                 }
448         }
449 }
450