2 // System.Web.UI.TemplateControl.cs
5 // Duncan Mak (duncan@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
9 // (C) 2002 Ximian, Inc. (http://www.ximian.com)
10 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
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;
41 using System.Runtime.InteropServices;
43 using System.Collections.Generic;
44 using System.Collections.Concurrent;
46 namespace System.Web.UI
49 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
50 [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
51 public abstract class TemplateControl : Control, INamingContainer, IFilterResolutionService
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",
61 "Page_PreRenderComplete",
62 "Page_SaveStateComplete",
70 "Page_AbortTransaction",
71 "Page_CommitTransaction"};
73 const BindingFlags bflags = BindingFlags.Public |
74 BindingFlags.NonPublic |
75 BindingFlags.Instance;
77 string _appRelativeVirtualPath;
78 StringResourceData resource_data;
81 protected TemplateControl ()
83 TemplateControl = this;
90 [EditorBrowsable (EditorBrowsableState.Never)]
92 protected virtual int AutoHandlers {
97 [EditorBrowsable (EditorBrowsableState.Never)]
98 protected virtual bool SupportAutoEvents {
102 public string AppRelativeVirtualPath {
103 get { return _appRelativeVirtualPath; }
104 set { _appRelativeVirtualPath = value; }
111 protected virtual void Construct ()
115 protected LiteralControl CreateResourceBasedLiteralControl (int offset, int size, bool fAsciiOnly)
117 if (resource_data == null)
120 if (offset > resource_data.MaxOffset - size)
121 throw new ArgumentOutOfRangeException ("size");
123 IntPtr ptr = AddOffset (resource_data.Ptr, offset);
124 return new ResourceBasedLiteralControl (ptr, size);
128 public MethodInfo method;
129 public string methodName;
130 public EventInfo evt;
131 public bool noParams;
134 static SplitOrderedList<Type, ArrayList> auto_event_info = new SplitOrderedList<Type, ArrayList> (EqualityComparer<Type>.Default);
136 internal void WireupAutomaticEvents ()
138 if (!SupportAutoEvents || !AutoEventWireup)
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);
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);
151 evinfo.evt.AddEventHandler (this, Delegate.CreateDelegate (typeof (EventHandler), this, evinfo.method));
155 ArrayList CollectAutomaticEventInfo () {
156 ArrayList events = new ArrayList ();
158 foreach (string methodName in methodNames) {
159 MethodInfo method = null;
161 for (type = GetType (); type.Assembly != _System_Web_Assembly; type = type.BaseType) {
162 method = type.GetMethod (methodName, bflags);
169 if (method.DeclaringType != type) {
170 if (!method.IsPublic && !method.IsFamilyOrAssembly &&
171 !method.IsFamilyAndAssembly && !method.IsFamily)
175 if (method.ReturnType != typeof (void))
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)))
186 int pos = methodName.IndexOf ('_');
187 string eventName = methodName.Substring (pos + 1);
188 EventInfo evt = type.GetEvent (eventName);
190 /* This should never happen */
194 EvtInfo evinfo = new EvtInfo ();
195 evinfo.method = method;
196 evinfo.methodName = methodName;
198 evinfo.noParams = noParams;
206 [EditorBrowsable (EditorBrowsableState.Never)]
207 protected virtual void FrameworkInitialize ()
211 Type GetTypeFromControlPath (string virtualPath)
213 if (virtualPath == null)
214 throw new ArgumentNullException ("virtualPath");
216 string vpath = UrlUtils.Combine (TemplateSourceDirectory, virtualPath);
217 return BuildManager.GetCompiledType (vpath);
220 public Control LoadControl (string virtualPath)
222 if (virtualPath == null)
223 throw new ArgumentNullException ("virtualPath");
224 Type type = GetTypeFromControlPath (virtualPath);
226 return LoadControl (type, null);
229 public Control LoadControl (Type type, object[] parameters)
231 object [] attrs = 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;
244 object control = Activator.CreateInstance (type, parameters);
245 if (control is UserControl)
246 ((UserControl) control).InitializeAsUserControl (Page);
248 return (Control) control;
251 public ITemplate LoadTemplate (string virtualPath)
253 if (virtualPath == null)
254 throw new ArgumentNullException ("virtualPath");
255 Type t = GetTypeFromControlPath (virtualPath);
256 return new SimpleTemplate (t);
259 protected virtual void OnAbortTransaction (EventArgs e)
261 EventHandler eh = Events [abortTransaction] as EventHandler;
266 protected virtual void OnCommitTransaction (EventArgs e)
268 EventHandler eh = Events [commitTransaction] as EventHandler;
273 protected virtual void OnError (EventArgs e)
275 EventHandler eh = Events [error] as EventHandler;
280 public Control ParseControl (string content)
283 throw new ArgumentNullException ("content");
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);
294 TemplateControl parsedControl = Activator.CreateInstance (control, null) as TemplateControl;
295 if (parsedControl == null)
298 if (this is System.Web.UI.Page)
299 parsedControl.Page = (System.Web.UI.Page) this;
300 parsedControl.FrameworkInitialize ();
302 Control ret = new Control ();
303 int count = parsedControl.Controls.Count;
304 Control[] parsedControlControls = new Control [count];
305 parsedControl.Controls.CopyTo (parsedControlControls, 0);
307 for (int i = 0; i < count; i++)
308 ret.Controls.Add (parsedControlControls [i]);
310 parsedControl = null;
314 [MonoTODO ("Parser filters not implemented yet. Calls ParseControl (string) for now.")]
315 public Control ParseControl (string content, bool ignoreParserFilter)
317 return ParseControl (content);
320 [EditorBrowsable (EditorBrowsableState.Never)]
321 public object ReadStringResource ()
323 return ReadStringResource (GetType ());
326 class StringResourceData {
329 public int MaxOffset;
332 protected object GetGlobalResourceObject (string className, string resourceKey)
334 return HttpContext.GetGlobalResourceObject (className, resourceKey);
337 protected object GetGlobalResourceObject (string className, string resourceKey, Type objType, string propName)
339 if (String.IsNullOrEmpty (resourceKey) || String.IsNullOrEmpty (propName) ||
340 String.IsNullOrEmpty (className) || objType == null)
343 object globalObject = GetGlobalResourceObject (className, resourceKey);
344 if (globalObject == null)
347 TypeConverter converter = TypeDescriptor.GetProperties (objType) [propName].Converter;
348 if (converter == null || !converter.CanConvertFrom (globalObject.GetType ()))
351 return converter.ConvertFrom (globalObject);
354 protected object GetLocalResourceObject (string resourceKey)
356 return HttpContext.GetLocalResourceObject (VirtualPathUtility.ToAbsolute (this.AppRelativeVirtualPath),
360 protected object GetLocalResourceObject (string resourceKey, Type objType, string propName)
362 if (String.IsNullOrEmpty (resourceKey) || String.IsNullOrEmpty (propName) || objType == null)
365 object localObject = GetLocalResourceObject (resourceKey);
366 if (localObject == null)
369 TypeConverter converter = TypeDescriptor.GetProperties (objType) [propName].Converter;
370 if (converter == null || !converter.CanConvertFrom (localObject.GetType ()))
373 return converter.ConvertFrom (localObject);
376 internal override TemplateControl TemplateControlInternal {
380 [EditorBrowsable (EditorBrowsableState.Never)]
381 public static object ReadStringResource (Type t)
383 StringResourceData data = new StringResourceData ();
384 if (ICalls.GetUnmanagedResourcesPtr (t.Assembly, out data.Ptr, out data.Length))
387 throw new HttpException ("Unable to load the string resources.");
390 [EditorBrowsable (EditorBrowsableState.Never)]
391 protected void SetStringResourcePointer (object stringResourcePointer,
392 int maxResourceOffset)
394 StringResourceData rd = stringResourcePointer as StringResourceData;
398 if (maxResourceOffset < 0 || maxResourceOffset > rd.Length)
399 throw new ArgumentOutOfRangeException ("maxResourceOffset");
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;
407 static IntPtr AddOffset (IntPtr ptr, int offset)
412 if (IntPtr.Size == 4) {
413 int p = ptr.ToInt32 () + offset;
414 ptr = new IntPtr (p);
416 long p = ptr.ToInt64 () + offset;
417 ptr = new IntPtr (p);
422 [EditorBrowsable (EditorBrowsableState.Never)]
423 protected void WriteUTF8ResourceString (HtmlTextWriter output, int offset, int size, bool fAsciiOnly)
425 if (resource_data == null)
428 throw new ArgumentNullException ("output");
429 if (offset > resource_data.MaxOffset - size)
430 throw new ArgumentOutOfRangeException ("size");
433 IntPtr ptr = AddOffset (resource_data.Ptr, offset);
434 HttpWriter writer = output.GetHttpWriter ();
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));
444 writer.WriteUTF8Ptr (ptr, size);
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); }
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); }
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); }
471 class SimpleTemplate : ITemplate
475 public SimpleTemplate (Type type)
480 public void InstantiateIn (Control control)
482 Control template = Activator.CreateInstance (type) as Control;
483 template.SetBindingContainer (false);
484 control.Controls.Add (template);
488 protected internal object Eval (string expression)
490 return DataBinder.Eval (Page.GetDataItem(), expression);
493 protected internal string Eval (string expression, string format)
495 return DataBinder.Eval (Page.GetDataItem(), expression, format);
498 protected internal object XPath (string xpathexpression)
500 return XPathBinder.Eval (Page.GetDataItem(), xpathexpression);
503 protected internal object XPath (string xpathexpression, IXmlNamespaceResolver resolver)
505 return XPathBinder.Eval (Page.GetDataItem (), xpathexpression, null, resolver);
508 protected internal string XPath (string xpathexpression, string format)
510 return XPathBinder.Eval (Page.GetDataItem(), xpathexpression, format);
513 protected internal string XPath (string xpathexpression, string format, IXmlNamespaceResolver resolver)
515 return XPathBinder.Eval (Page.GetDataItem (), xpathexpression, format, resolver);
518 protected internal IEnumerable XPathSelect (string xpathexpression)
520 return XPathBinder.Select (Page.GetDataItem(), xpathexpression);
523 protected internal IEnumerable XPathSelect (string xpathexpression, IXmlNamespaceResolver resolver)
525 return XPathBinder.Select (Page.GetDataItem (), xpathexpression, resolver);
528 // IFilterResolutionService
530 [MonoTODO ("Not implemented")]
531 int IFilterResolutionService.CompareFilters (string filter1, string filter2)
533 throw new NotImplementedException ();
536 [MonoTODO ("Not implemented")]
537 bool IFilterResolutionService.EvaluateFilter (string filterName)
539 throw new NotImplementedException ();