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