[asp.net] Fix for bug #685267. ClientScriptManager generates correct post-back event...
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateControl.cs
index 69057c11aca4f20ff25593618256bc6500ff8448..6480b008bd983488f7a684fb58121c89be6db3d4 100644 (file)
@@ -7,7 +7,7 @@
 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
 //
 // (C) 2002 Ximian, Inc. (http://www.ximian.com)
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -33,33 +33,34 @@ using System.Collections;
 using System.ComponentModel;
 using System.Reflection;
 using System.Security.Permissions;
+using System.Threading;
 using System.Web.Compilation;
 using System.Web.Util;
 using System.Xml;
-
-namespace System.Web.UI {
-
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+
+namespace System.Web.UI
+{
        // CAS
        [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
-#if NET_2_0
-       public abstract class TemplateControl : Control, INamingContainer, IFilterResolutionService {
-#else
-       public abstract class TemplateControl : Control, INamingContainer {
-#endif
+       public abstract class TemplateControl : Control, INamingContainer, IFilterResolutionService
+       {
                static readonly Assembly _System_Web_Assembly = typeof (TemplateControl).Assembly;
                static object abortTransaction = new object ();
                static object commitTransaction = new object ();
                static object error = new object ();
                static string [] methodNames = { "Page_Init",
-#if NET_2_0
                                                 "Page_PreInit",
                                                 "Page_PreLoad",
                                                 "Page_LoadComplete",
                                                 "Page_PreRenderComplete",
                                                 "Page_SaveStateComplete",
                                                 "Page_InitComplete",
-#endif
                                                 "Page_Load",
                                                 "Page_DataBind",
                                                 "Page_PreRender",
@@ -73,9 +74,13 @@ namespace System.Web.UI {
                                            BindingFlags.NonPublic |
                                            BindingFlags.Instance;
 
+               string _appRelativeVirtualPath;
+               StringResourceData resource_data;
+               
                #region Constructor
                protected TemplateControl ()
                {
+                       TemplateControl = this;
                        Construct ();
                }
 
@@ -83,9 +88,7 @@ namespace System.Web.UI {
 
                #region Properties
                [EditorBrowsable (EditorBrowsableState.Never)]
-#if NET_2_0
                [Obsolete]
-#endif
                protected virtual int AutoHandlers {
                        get { return 0; }
                        set { }
@@ -96,14 +99,10 @@ namespace System.Web.UI {
                        get { return true; }
                }
 
-#if NET_2_0
-               
-               [MonoTODO ("NotImplementedException")]
                public string AppRelativeVirtualPath {
-                       get { throw new NotImplementedException(); }
-                       set { throw new NotImplementedException (); }
+                       get { return _appRelativeVirtualPath; }
+                       set { _appRelativeVirtualPath = value; }
                }
-#endif
 
                #endregion
 
@@ -113,19 +112,49 @@ namespace System.Web.UI {
                {
                }
 
-               [MonoTODO ("Not implemented")]
-               protected LiteralControl CreateResourceBasedLiteralControl (int offset,
-                                                                                   int size,
-                                                                                   bool fAsciiOnly)
+               protected LiteralControl CreateResourceBasedLiteralControl (int offset, int size, bool fAsciiOnly)
                {
-                       return null;
+                       if (resource_data == null)
+                               return null;
+
+                       if (offset > resource_data.MaxOffset - size)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       IntPtr ptr = AddOffset (resource_data.Ptr, offset);
+                       return new ResourceBasedLiteralControl (ptr, size);
+               }
+
+               class EvtInfo {
+                       public MethodInfo method;
+                       public string methodName;
+                       public EventInfo evt;
+                       public bool noParams;
                }
 
+               static SplitOrderedList<Type, ArrayList> auto_event_info = new SplitOrderedList<Type, ArrayList> (EqualityComparer<Type>.Default);
+
                internal void WireupAutomaticEvents ()
                {
                        if (!SupportAutoEvents || !AutoEventWireup)
                                return;
 
+                       /* Avoid expensive reflection operations by computing the event info only once */
+                       Type type = GetType ();
+                       ArrayList events = auto_event_info.InsertOrGet ((uint)type.GetHashCode (), type, null, CollectAutomaticEventInfo);
+
+                       for (int i = 0; i < events.Count; ++i) {
+                               EvtInfo evinfo = (EvtInfo)events [i];
+                               if (evinfo.noParams) {
+                                       NoParamsInvoker npi = new NoParamsInvoker (this, evinfo.method);
+                                       evinfo.evt.AddEventHandler (this, npi.FakeDelegate);
+                               } else
+                                       evinfo.evt.AddEventHandler (this, Delegate.CreateDelegate (typeof (EventHandler), this, evinfo.method));
+                       }
+               }
+
+               ArrayList CollectAutomaticEventInfo () {
+                       ArrayList events = new ArrayList ();
+
                        foreach (string methodName in methodNames) {
                                MethodInfo method = null;
                                Type type;
@@ -154,7 +183,7 @@ namespace System.Web.UI {
                                    parms [1].ParameterType != typeof (EventArgs)))
                                    continue;
 
-                               int pos = methodName.IndexOf ("_");
+                               int pos = methodName.IndexOf ('_');
                                string eventName = methodName.Substring (pos + 1);
                                EventInfo evt = type.GetEvent (eventName);
                                if (evt == null) {
@@ -162,14 +191,16 @@ namespace System.Web.UI {
                                        continue;
                                }
 
-                               if (noParams) {
-                                       NoParamsInvoker npi = new NoParamsInvoker (this, methodName);
-                                       evt.AddEventHandler (this, npi.FakeDelegate);
-                               } else {
-                                       evt.AddEventHandler (this, Delegate.CreateDelegate (
-                                                       typeof (EventHandler), this, methodName));
-                               }
+                               EvtInfo evinfo = new EvtInfo ();
+                               evinfo.method = method;
+                               evinfo.methodName = methodName;
+                               evinfo.evt = evt;
+                               evinfo.noParams = noParams;
+
+                               events.Add (evinfo);
                        }
+
+                       return events;
                }
 
                [EditorBrowsable (EditorBrowsableState.Never)]
@@ -183,31 +214,34 @@ namespace System.Web.UI {
                                throw new ArgumentNullException ("virtualPath");
 
                        string vpath = UrlUtils.Combine (TemplateSourceDirectory, virtualPath);
-                       string realpath = Context.Request.MapPath (vpath);
-                       return UserControlParser.GetCompiledType (vpath, realpath, Context);
+                       return BuildManager.GetCompiledType (vpath);
                }
 
                public Control LoadControl (string virtualPath)
                {
-#if NET_2_0
                        if (virtualPath == null)
                                throw new ArgumentNullException ("virtualPath");
-#else
-                       if (virtualPath == null)
-                               throw new HttpException ("virtualPath is null");
-#endif
                        Type type = GetTypeFromControlPath (virtualPath);
-                       object [] attrs = type.GetCustomAttributes (typeof (PartialCachingAttribute), true);
+                       
+                       return LoadControl (type, null);
+               }
+
+               public Control LoadControl (Type type, object[] parameters) 
+               {
+                       object [] attrs = null;
+
+                       if (type != null)
+                               type.GetCustomAttributes (typeof (PartialCachingAttribute), true);
                        if (attrs != null && attrs.Length == 1) {
                                PartialCachingAttribute attr = (PartialCachingAttribute) attrs [0];
-                               PartialCachingControl ctrl = new PartialCachingControl (type);
+                               PartialCachingControl ctrl = new PartialCachingControl (type, parameters);
                                ctrl.VaryByParams = attr.VaryByParams;
                                ctrl.VaryByControls = attr.VaryByControls;
                                ctrl.VaryByCustom = attr.VaryByCustom;
                                return ctrl;
                        }
 
-                       object control = Activator.CreateInstance (type);
+                       object control = Activator.CreateInstance (type, parameters);
                        if (control is UserControl)
                                ((UserControl) control).InitializeAsUserControl (Page);
 
@@ -216,13 +250,8 @@ namespace System.Web.UI {
 
                public ITemplate LoadTemplate (string virtualPath)
                {
-#if NET_2_0
                        if (virtualPath == null)
                                throw new ArgumentNullException ("virtualPath");
-#else
-                       if (virtualPath == null)
-                               throw new HttpException ("virtualPath is null");
-#endif
                        Type t = GetTypeFromControlPath (virtualPath);
                        return new SimpleTemplate (t);
                }
@@ -248,68 +277,171 @@ namespace System.Web.UI {
                                eh (this, e);
                }
 
-               [MonoTODO ("Not implemented, always returns null")]
                public Control ParseControl (string content)
                {
                        if (content == null)
                                throw new ArgumentNullException ("content");
 
-                       return null;
+                       // FIXME: This method needs to be rewritten in some sane way - the way it is now,
+                       // is a kludge. New version should not use
+                       // UserControlParser.GetCompiledType, but instead resort to some other way
+                       // of creating the content (template instantiation? BuildManager? TBD)
+                       TextReader reader = new StringReader (content);
+                       Type control = UserControlParser.GetCompiledType (reader, content.GetHashCode (), HttpContext.Current);
+                       if (control == null)
+                               return null;
+
+                       TemplateControl parsedControl = Activator.CreateInstance (control, null) as TemplateControl;
+                       if (parsedControl == null)
+                               return null;
+
+                       if (this is System.Web.UI.Page)
+                               parsedControl.Page = (System.Web.UI.Page) this;
+                       parsedControl.FrameworkInitialize ();
+                       
+                       Control ret = new Control ();
+                       int count = parsedControl.Controls.Count;
+                       Control[] parsedControlControls = new Control [count];
+                       parsedControl.Controls.CopyTo (parsedControlControls, 0);
+
+                       for (int i = 0; i < count; i++)
+                               ret.Controls.Add (parsedControlControls [i]);
+
+                       parsedControl = null;
+                       return ret;
+               }
+
+               [MonoTODO ("Parser filters not implemented yet. Calls ParseControl (string) for now.")]
+               public Control ParseControl (string content, bool ignoreParserFilter)
+               {
+                       return ParseControl (content);
                }
-
+       
                [EditorBrowsable (EditorBrowsableState.Never)]
-               public 
-#if !NET_2_0
-               static
-#endif
-               object ReadStringResource ()
+               public object ReadStringResource ()
                {
-                       throw new NotSupportedException ();
+                       return ReadStringResource (GetType ());
+               }
+
+               class StringResourceData {
+                       public IntPtr Ptr;
+                       public int Length;
+                       public int MaxOffset;
                }
 
-#if NET_2_0
                protected object GetGlobalResourceObject (string className, string resourceKey)
                {
                        return HttpContext.GetGlobalResourceObject (className, resourceKey);
                }
 
-               [MonoTODO ("Not implemented")]
                protected object GetGlobalResourceObject (string className, string resourceKey, Type objType, string propName)
                {
-                       // FIXME: not sure how to implement that one yet
-                       throw new NotSupportedException();
+                       if (String.IsNullOrEmpty (resourceKey) || String.IsNullOrEmpty (propName) ||
+                           String.IsNullOrEmpty (className) || objType == null)
+                               return null;
+
+                       object globalObject = GetGlobalResourceObject (className, resourceKey);
+                       if (globalObject == null)
+                               return null;
+                       
+                       TypeConverter converter = TypeDescriptor.GetProperties (objType) [propName].Converter;
+                       if (converter == null || !converter.CanConvertFrom (globalObject.GetType ()))
+                               return null;
+                       
+                       return converter.ConvertFrom (globalObject);
                }
 
                protected object GetLocalResourceObject (string resourceKey)
                {
-                       return HttpContext.GetLocalResourceObject (Context.Request.CurrentExecutionFilePath, resourceKey);
+                       return HttpContext.GetLocalResourceObject (VirtualPathUtility.ToAbsolute (this.AppRelativeVirtualPath),
+                                                                  resourceKey);
                }
                
                protected object GetLocalResourceObject (string resourceKey, Type objType, string propName)
                {
-                       // FIXME: not sure how to implement that one yet
-                       throw new NotSupportedException();
+                       if (String.IsNullOrEmpty (resourceKey) || String.IsNullOrEmpty (propName) || objType == null)
+                               return null;
+
+                       object localObject = GetLocalResourceObject (resourceKey);
+                       if (localObject == null)
+                               return null;
+                       
+                       TypeConverter converter = TypeDescriptor.GetProperties (objType) [propName].Converter;
+                       if (converter == null || !converter.CanConvertFrom (localObject.GetType ()))
+                               return null;
+                       
+                       return converter.ConvertFrom (localObject);
+               }
+
+               internal override TemplateControl TemplateControlInternal {
+                       get { return this; }
                }
-#endif
                
                [EditorBrowsable (EditorBrowsableState.Never)]
                public static object ReadStringResource (Type t)
                {
-                       throw new NotSupportedException ();
+                       StringResourceData data = new StringResourceData ();
+                       if (ICalls.GetUnmanagedResourcesPtr (t.Assembly, out data.Ptr, out data.Length))
+                               return data;
+
+                       throw new HttpException ("Unable to load the string resources.");
                }
 
-               [MonoTODO ("Not implemented, does nothing")]
                [EditorBrowsable (EditorBrowsableState.Never)]
                protected void SetStringResourcePointer (object stringResourcePointer,
                                                         int maxResourceOffset)
                {
+                       StringResourceData rd = stringResourcePointer as StringResourceData;
+                       if (rd == null)
+                               return;
+
+                       if (maxResourceOffset < 0 || maxResourceOffset > rd.Length)
+                               throw new ArgumentOutOfRangeException ("maxResourceOffset");
+
+                       resource_data = new StringResourceData ();
+                       resource_data.Ptr = rd.Ptr;
+                       resource_data.Length = rd.Length;
+                       resource_data.MaxOffset = maxResourceOffset > 0 ? Math.Min (maxResourceOffset, rd.Length) : rd.Length;
+               }
+
+               static IntPtr AddOffset (IntPtr ptr, int offset)
+               {
+                       if (offset == 0)
+                               return ptr;
+
+                       if (IntPtr.Size == 4) {
+                               int p = ptr.ToInt32 () + offset;
+                               ptr = new IntPtr (p);
+                       } else {
+                               long p = ptr.ToInt64 () + offset;
+                               ptr = new IntPtr (p);
+                       }
+                       return ptr;
                }
 
-               [MonoTODO ("Not implemented, does nothing")]
                [EditorBrowsable (EditorBrowsableState.Never)]
-               protected void WriteUTF8ResourceString (HtmlTextWriter output, int offset,
-                                                       int size, bool fAsciiOnly)
+               protected void WriteUTF8ResourceString (HtmlTextWriter output, int offset, int size, bool fAsciiOnly)
                {
+                       if (resource_data == null)
+                               return; // throw?
+                       if (output == null)
+                               throw new ArgumentNullException ("output");
+                       if (offset > resource_data.MaxOffset - size)
+                               throw new ArgumentOutOfRangeException ("size");
+
+                       //TODO: fAsciiOnly?
+                       IntPtr ptr = AddOffset (resource_data.Ptr, offset);
+                       HttpWriter writer = output.GetHttpWriter ();
+                       
+                       if (writer == null || writer.Response.ContentEncoding.CodePage != 65001) {
+                               byte [] bytes = new byte [size];
+                               Marshal.Copy (ptr, bytes, 0, size);
+                               output.Write (Encoding.UTF8.GetString (bytes));
+                               bytes = null;
+                               return;
+                       }
+
+                       writer.WriteUTF8Ptr (ptr, size);
                }
 
                #endregion
@@ -353,7 +485,6 @@ namespace System.Web.UI {
                        }
                }
 
-#if NET_2_0
                protected internal object Eval (string expression)
                {
                        return DataBinder.Eval (Page.GetDataItem(), expression);
@@ -407,6 +538,5 @@ namespace System.Web.UI {
                {
                        throw new NotImplementedException ();
                }
-#endif
        }
 }