5804de26e91fa6f9e8c8084edae5676ffb9edaa1
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateControl.cs
1 //
2 // System.Web.UI.TemplateControl.cs
3 //
4 // Authors:
5 //   Duncan Mak  (duncan@ximian.com)
6 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
8 //
9 // (C) 2002 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
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.Collections;
33 using System.ComponentModel;
34 using System.Reflection;
35 using System.Security.Permissions;
36 using System.Web.Compilation;
37 using System.Web.Util;
38 using System.Xml;
39 using System.IO;
40 using System.Runtime.InteropServices;
41 using System.Text;
42
43 namespace System.Web.UI {
44
45         // CAS
46         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
47         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
48 #if NET_2_0
49         public abstract class TemplateControl : Control, INamingContainer, IFilterResolutionService {
50 #else
51         public abstract class TemplateControl : Control, INamingContainer {
52 #endif
53                 static readonly Assembly _System_Web_Assembly = typeof (TemplateControl).Assembly;
54                 static object abortTransaction = new object ();
55                 static object commitTransaction = new object ();
56                 static object error = new object ();
57                 static string [] methodNames = { "Page_Init",
58 #if NET_2_0
59                                                  "Page_PreInit",
60                                                  "Page_PreLoad",
61                                                  "Page_LoadComplete",
62                                                  "Page_PreRenderComplete",
63                                                  "Page_SaveStateComplete",
64                                                  "Page_InitComplete",
65 #endif
66                                                  "Page_Load",
67                                                  "Page_DataBind",
68                                                  "Page_PreRender",
69                                                  "Page_Disposed",
70                                                  "Page_Error",
71                                                  "Page_Unload",
72                                                  "Page_AbortTransaction",
73                                                  "Page_CommitTransaction"};
74
75                 const BindingFlags bflags = BindingFlags.Public |
76                                             BindingFlags.NonPublic |
77                                             BindingFlags.Instance;
78
79 #if NET_2_0
80                 string _appRelativeVirtualPath;
81 #endif
82                 StringResourceData resource_data;
83                 
84                 #region Constructor
85                 protected TemplateControl ()
86                 {
87 #if NET_2_0
88                         TemplateControl = this;
89 #endif
90                         Construct ();
91                 }
92
93                 #endregion
94
95                 #region Properties
96                 [EditorBrowsable (EditorBrowsableState.Never)]
97 #if NET_2_0
98                 [Obsolete]
99 #endif
100                 protected virtual int AutoHandlers {
101                         get { return 0; }
102                         set { }
103                 }
104
105                 [EditorBrowsable (EditorBrowsableState.Never)]
106                 protected virtual bool SupportAutoEvents {
107                         get { return true; }
108                 }
109
110 #if NET_2_0
111                 public string AppRelativeVirtualPath {
112                         get { return _appRelativeVirtualPath; }
113                         set { _appRelativeVirtualPath = value; }
114                 }
115 #endif
116
117                 #endregion
118
119                 #region Methods
120
121                 protected virtual void Construct ()
122                 {
123                 }
124
125                 protected LiteralControl CreateResourceBasedLiteralControl (int offset, int size, bool fAsciiOnly)
126                 {
127                         if (resource_data == null)
128                                 return null;
129
130                         if (offset > resource_data.MaxOffset - size)
131                                 throw new ArgumentOutOfRangeException ("size");
132
133                         IntPtr ptr = AddOffset (resource_data.Ptr, offset);
134                         return new ResourceBasedLiteralControl (ptr, size);
135                 }
136
137                 class EvtInfo {
138                         public MethodInfo method;
139                         public string methodName;
140                         public EventInfo evt;
141                         public bool noParams;
142                 }
143
144                 static Hashtable auto_event_info;
145                 static object auto_event_info_monitor = new Object ();
146
147                 internal void WireupAutomaticEvents ()
148                 {
149                         if (!SupportAutoEvents || !AutoEventWireup)
150                                 return;
151
152                         ArrayList events = null;
153
154                         /* Avoid expensive reflection operations by computing the event info only once */
155                         lock (auto_event_info_monitor) {
156                                 if (auto_event_info == null)
157                                         auto_event_info = new Hashtable ();
158                                 events = (ArrayList)auto_event_info [GetType ()];
159                                 if (events == null) {
160                                         events = CollectAutomaticEventInfo ();
161                                         auto_event_info [GetType ()] = events;
162                                 }
163                         }
164
165                         for (int i = 0; i < events.Count; ++i) {
166                                 EvtInfo evinfo = (EvtInfo)events [i];
167                                 if (evinfo.noParams) {
168                                         NoParamsInvoker npi = new NoParamsInvoker (this, evinfo.method);
169                                         evinfo.evt.AddEventHandler (this, npi.FakeDelegate);
170                                 } else {
171                                         evinfo.evt.AddEventHandler (this, Delegate.CreateDelegate (
172 #if NET_2_0
173                                                         typeof (EventHandler), this, evinfo.method));
174 #else
175                                                         typeof (EventHandler), this, evinfo.methodName));
176 #endif
177                                 }
178                         }
179                 }
180
181                 ArrayList CollectAutomaticEventInfo () {
182                         ArrayList events = new ArrayList ();
183
184                         foreach (string methodName in methodNames) {
185                                 MethodInfo method = null;
186                                 Type type;
187                                 for (type = GetType (); type.Assembly != _System_Web_Assembly; type = type.BaseType) {
188                                         method = type.GetMethod (methodName, bflags);
189                                         if (method != null)
190                                                 break;
191                                 }
192                                 if (method == null)
193                                         continue;
194
195                                 if (method.DeclaringType != type) {
196                                         if (!method.IsPublic && !method.IsFamilyOrAssembly &&
197                                             !method.IsFamilyAndAssembly && !method.IsFamily)
198                                                 continue;
199                                 }
200
201                                 if (method.ReturnType != typeof (void))
202                                         continue;
203
204                                 ParameterInfo [] parms = method.GetParameters ();
205                                 int length = parms.Length;
206                                 bool noParams = (length == 0);
207                                 if (!noParams && (length != 2 ||
208                                     parms [0].ParameterType != typeof (object) ||
209                                     parms [1].ParameterType != typeof (EventArgs)))
210                                     continue;
211
212                                 int pos = methodName.IndexOf ('_');
213                                 string eventName = methodName.Substring (pos + 1);
214                                 EventInfo evt = type.GetEvent (eventName);
215                                 if (evt == null) {
216                                         /* This should never happen */
217                                         continue;
218                                 }
219
220                                 EvtInfo evinfo = new EvtInfo ();
221                                 evinfo.method = method;
222                                 evinfo.methodName = methodName;
223                                 evinfo.evt = evt;
224                                 evinfo.noParams = noParams;
225
226                                 events.Add (evinfo);
227                         }
228
229                         return events;
230                 }
231
232                 [EditorBrowsable (EditorBrowsableState.Never)]
233                 protected virtual void FrameworkInitialize ()
234                 {
235                 }
236
237                 Type GetTypeFromControlPath (string virtualPath)
238                 {
239                         if (virtualPath == null)
240                                 throw new ArgumentNullException ("virtualPath");
241
242                         string vpath = UrlUtils.Combine (TemplateSourceDirectory, virtualPath);
243 #if NET_2_0
244                         return BuildManager.GetCompiledType (vpath);
245 #else
246                         string realpath = Context.Request.MapPath (vpath);
247                         return UserControlParser.GetCompiledType (vpath, realpath, Context);
248 #endif
249                 }
250
251                 public Control LoadControl (string virtualPath)
252                 {
253 #if NET_2_0
254                         if (virtualPath == null)
255                                 throw new ArgumentNullException ("virtualPath");
256 #else
257                         if (virtualPath == null)
258                                 throw new HttpException ("virtualPath is null");
259 #endif
260                         Type type = GetTypeFromControlPath (virtualPath);
261                         
262                         return LoadControl (type, null);
263                 }
264
265                 public Control LoadControl (Type type, object[] parameters) 
266                 {
267                         object [] attrs = null;
268
269                         if (type != null)
270                                 type.GetCustomAttributes (typeof (PartialCachingAttribute), true);
271                         if (attrs != null && attrs.Length == 1) {
272                                 PartialCachingAttribute attr = (PartialCachingAttribute) attrs [0];
273                                 PartialCachingControl ctrl = new PartialCachingControl (type, parameters);
274                                 ctrl.VaryByParams = attr.VaryByParams;
275                                 ctrl.VaryByControls = attr.VaryByControls;
276                                 ctrl.VaryByCustom = attr.VaryByCustom;
277                                 return ctrl;
278                         }
279
280                         object control = Activator.CreateInstance (type, parameters);
281                         if (control is UserControl)
282                                 ((UserControl) control).InitializeAsUserControl (Page);
283
284                         return (Control) control;
285                 }
286
287                 public ITemplate LoadTemplate (string virtualPath)
288                 {
289 #if NET_2_0
290                         if (virtualPath == null)
291                                 throw new ArgumentNullException ("virtualPath");
292 #else
293                         if (virtualPath == null)
294                                 throw new HttpException ("virtualPath is null");
295 #endif
296                         Type t = GetTypeFromControlPath (virtualPath);
297                         return new SimpleTemplate (t);
298                 }
299
300                 protected virtual void OnAbortTransaction (EventArgs e)
301                 {
302                         EventHandler eh = Events [abortTransaction] as EventHandler;
303                         if (eh != null)
304                                 eh (this, e);
305                 }
306
307                 protected virtual void OnCommitTransaction (EventArgs e)
308                 {
309                         EventHandler eh = Events [commitTransaction] as EventHandler;
310                         if (eh != null)
311                                 eh (this, e);
312                 }
313
314                 protected virtual void OnError (EventArgs e)
315                 {
316                         EventHandler eh = Events [error] as EventHandler;
317                         if (eh != null)
318                                 eh (this, e);
319                 }
320
321 #if !NET_2_0
322                 [MonoTODO ("Not implemented, always returns null")]
323 #endif
324                 public Control ParseControl (string content)
325                 {
326                         if (content == null)
327                                 throw new ArgumentNullException ("content");
328
329 #if NET_2_0
330                         // FIXME: This method needs to be rewritten in some sane way - the way it is now,
331                         // is a kludge. New version should not use
332                         // UserControlParser.GetCompiledType, but instead resort to some other way
333                         // of creating the content (template instantiation? BuildManager? TBD)
334                         TextReader reader = new StringReader (content);
335                         Type control = UserControlParser.GetCompiledType (reader, content.GetHashCode (), HttpContext.Current);
336                         if (control == null)
337                                 return null;
338
339                         TemplateControl parsedControl = Activator.CreateInstance (control, null) as TemplateControl;
340                         if (parsedControl == null)
341                                 return null;
342
343                         if (this is System.Web.UI.Page)
344                                 parsedControl.Page = (System.Web.UI.Page) this;
345                         parsedControl.FrameworkInitialize ();
346                         
347                         Control ret = new Control ();
348                         int count = parsedControl.Controls.Count;
349                         Control[] parsedControlControls = new Control [count];
350                         parsedControl.Controls.CopyTo (parsedControlControls, 0);
351
352                         for (int i = 0; i < count; i++)
353                                 ret.Controls.Add (parsedControlControls [i]);
354
355                         parsedControl = null;
356                         return ret;
357 #else
358                         return null;
359 #endif
360                 }
361
362 #if NET_2_0
363                 [MonoTODO ("Parser filters not implemented yet. Calls ParseControl (string) for now.")]
364                 public Control ParseControl (string content, bool ignoreParserFilter)
365                 {
366                         return ParseControl (content);
367                 }
368 #endif
369         
370 #if NET_2_0
371                 [EditorBrowsable (EditorBrowsableState.Never)]
372                 public object ReadStringResource ()
373                 {
374                         return ReadStringResource (GetType ());
375                 }
376 #endif
377
378                 class StringResourceData {
379                         public IntPtr Ptr;
380                         public int Length;
381                         public int MaxOffset;
382                 }
383
384 #if NET_2_0
385                 protected object GetGlobalResourceObject (string className, string resourceKey)
386                 {
387                         return HttpContext.GetGlobalResourceObject (className, resourceKey);
388                 }
389
390                 protected object GetGlobalResourceObject (string className, string resourceKey, Type objType, string propName)
391                 {
392                         if (String.IsNullOrEmpty (resourceKey) || String.IsNullOrEmpty (propName) ||
393                             String.IsNullOrEmpty (className) || objType == null)
394                                 return null;
395
396                         object globalObject = GetGlobalResourceObject (className, resourceKey);
397                         if (globalObject == null)
398                                 return null;
399                         
400                         TypeConverter converter = TypeDescriptor.GetProperties (objType) [propName].Converter;
401                         if (converter == null || !converter.CanConvertFrom (globalObject.GetType ()))
402                                 return null;
403                         
404                         return converter.ConvertFrom (globalObject);
405                 }
406
407                 protected object GetLocalResourceObject (string resourceKey)
408                 {
409                         return HttpContext.GetLocalResourceObject (VirtualPathUtility.ToAbsolute (this.AppRelativeVirtualPath),
410                                                                    resourceKey);
411                 }
412                 
413                 protected object GetLocalResourceObject (string resourceKey, Type objType, string propName)
414                 {
415                         if (String.IsNullOrEmpty (resourceKey) || String.IsNullOrEmpty (propName) || objType == null)
416                                 return null;
417
418                         object localObject = GetLocalResourceObject (resourceKey);
419                         if (localObject == null)
420                                 return null;
421                         
422                         TypeConverter converter = TypeDescriptor.GetProperties (objType) [propName].Converter;
423                         if (converter == null || !converter.CanConvertFrom (localObject.GetType ()))
424                                 return null;
425                         
426                         return converter.ConvertFrom (localObject);
427                 }
428
429                 internal override TemplateControl TemplateControlInternal {
430                         get { return this; }
431                 }
432 #endif
433                 
434                 [EditorBrowsable (EditorBrowsableState.Never)]
435                 public static object ReadStringResource (Type t)
436                 {
437                         StringResourceData data = new StringResourceData ();
438                         if (ICalls.GetUnmanagedResourcesPtr (t.Assembly, out data.Ptr, out data.Length))
439                                 return data;
440
441                         throw new HttpException ("Unable to load the string resources.");
442                 }
443
444                 [EditorBrowsable (EditorBrowsableState.Never)]
445                 protected void SetStringResourcePointer (object stringResourcePointer,
446                                                          int maxResourceOffset)
447                 {
448                         StringResourceData rd = stringResourcePointer as StringResourceData;
449                         if (rd == null)
450                                 return;
451
452                         if (maxResourceOffset < 0 || maxResourceOffset > rd.Length)
453                                 throw new ArgumentOutOfRangeException ("maxResourceOffset");
454
455                         resource_data = new StringResourceData ();
456                         resource_data.Ptr = rd.Ptr;
457                         resource_data.Length = rd.Length;
458                         resource_data.MaxOffset = maxResourceOffset > 0 ? Math.Min (maxResourceOffset, rd.Length) : rd.Length;
459                 }
460
461                 static IntPtr AddOffset (IntPtr ptr, int offset)
462                 {
463                         if (offset == 0)
464                                 return ptr;
465
466                         if (IntPtr.Size == 4) {
467                                 int p = ptr.ToInt32 () + offset;
468                                 ptr = new IntPtr (p);
469                         } else {
470                                 long p = ptr.ToInt64 () + offset;
471                                 ptr = new IntPtr (p);
472                         }
473                         return ptr;
474                 }
475
476                 [EditorBrowsable (EditorBrowsableState.Never)]
477                 protected void WriteUTF8ResourceString (HtmlTextWriter output, int offset, int size, bool fAsciiOnly)
478                 {
479                         if (resource_data == null)
480                                 return; // throw?
481                         if (output == null)
482                                 throw new ArgumentNullException ("output");
483                         if (offset > resource_data.MaxOffset - size)
484                                 throw new ArgumentOutOfRangeException ("size");
485
486                         //TODO: fAsciiOnly?
487                         IntPtr ptr = AddOffset (resource_data.Ptr, offset);
488                         HttpWriter writer = output.GetHttpWriter ();
489                         if (writer == null || writer.Response.ContentEncoding.CodePage != 65001) {
490                                 byte [] bytes = new byte [size];
491                                 Marshal.Copy (ptr, bytes, 0, size);
492                                 writer.Write (Encoding.UTF8.GetString (bytes));
493                                 bytes = null;
494                                 return;
495                         }
496
497                         writer.WriteUTF8Ptr (ptr, size);
498                 }
499
500                 #endregion
501
502                 #region Events
503
504                 [WebSysDescription ("Raised when the user aborts a transaction.")]
505                 public event EventHandler AbortTransaction {
506                         add { Events.AddHandler (abortTransaction, value); }
507                         remove { Events.RemoveHandler (abortTransaction, value); }
508                 }
509
510                 [WebSysDescription ("Raised when the user initiates a transaction.")]
511                 public event EventHandler CommitTransaction {
512                         add { Events.AddHandler (commitTransaction, value); }
513                         remove { Events.RemoveHandler (commitTransaction, value); }
514                 }
515
516                 [WebSysDescription ("Raised when an exception occurs that cannot be handled.")]
517                 public event EventHandler Error {
518                         add { Events.AddHandler (error, value); }
519                         remove { Events.RemoveHandler (error, value); }
520                 }
521
522                 #endregion
523
524                 class SimpleTemplate : ITemplate
525                 {
526                         Type type;
527
528                         public SimpleTemplate (Type type)
529                         {
530                                 this.type = type;
531                         }
532
533                         public void InstantiateIn (Control control)
534                         {
535                                 Control template = Activator.CreateInstance (type) as Control;
536                                 template.SetBindingContainer (false);
537                                 control.Controls.Add (template);
538                         }
539                 }
540
541 #if NET_2_0
542                 protected internal object Eval (string expression)
543                 {
544                         return DataBinder.Eval (Page.GetDataItem(), expression);
545                 }
546         
547                 protected internal string Eval (string expression, string format)
548                 {
549                         return DataBinder.Eval (Page.GetDataItem(), expression, format);
550                 }
551         
552                 protected internal object XPath (string xpathexpression)
553                 {
554                         return XPathBinder.Eval (Page.GetDataItem(), xpathexpression);
555                 }
556         
557                 protected internal object XPath (string xpathexpression, IXmlNamespaceResolver resolver)
558                 {
559                         return XPathBinder.Eval (Page.GetDataItem (), xpathexpression, null, resolver);
560                 }
561
562                 protected internal string XPath (string xpathexpression, string format)
563                 {
564                         return XPathBinder.Eval (Page.GetDataItem(), xpathexpression, format);
565                 }
566         
567                 protected internal string XPath (string xpathexpression, string format, IXmlNamespaceResolver resolver)
568                 {
569                         return XPathBinder.Eval (Page.GetDataItem (), xpathexpression, format, resolver);
570                 }
571
572                 protected internal IEnumerable XPathSelect (string xpathexpression)
573                 {
574                         return XPathBinder.Select (Page.GetDataItem(), xpathexpression);
575                 }
576
577                 protected internal IEnumerable XPathSelect (string xpathexpression, IXmlNamespaceResolver resolver)
578                 {
579                         return XPathBinder.Select (Page.GetDataItem (), xpathexpression, resolver);
580                 }
581
582                 // IFilterResolutionService
583
584                 [MonoTODO ("Not implemented")]
585                 int IFilterResolutionService.CompareFilters (string filter1, string filter2)
586                 {
587                         throw new NotImplementedException ();
588                 }
589
590                 [MonoTODO ("Not implemented")]
591                 bool IFilterResolutionService.EvaluateFilter (string filterName)
592                 {
593                         throw new NotImplementedException ();
594                 }
595 #endif
596         }
597 }