[asp.net] Fix for bug #685267. ClientScriptManager generates correct post-back event...
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateControl.jvm.cs
index ae0e731616c592a3e0b3e2737e11c48d28890d5f..28a3f0f79701368095398228423a5c8e96c451a4 100644 (file)
@@ -33,15 +33,16 @@ using System.Web.J2EE;
 using System.Xml;
 using vmw.common;
 using System.Web.Util;
+using System.Collections.Generic;
 
 namespace System.Web.UI {
 
        public abstract class TemplateControl : Control, INamingContainer
        {
-               static object abortTransaction = new object ();
-               static object commitTransaction = new object ();
-               static object error = new object ();
-               static string [] methodNames = { "Page_Init",
+               static readonly object abortTransaction = new object ();
+               static readonly object commitTransaction = new object ();
+               static readonly object error = new object ();
+               static readonly string [] methodNames = { "Page_Init",
 #if NET_2_0
                                                 "Page_PreInit",
                                                 "Page_PreLoad",
@@ -54,17 +55,57 @@ namespace System.Web.UI {
                                                 "Page_DataBind",
                                                 "Page_PreRender",
                                                 "Page_Disposed",
-                                                "Page_Error",
                                                 "Page_Unload",
+                                                "Page_Error",
                                                 "Page_AbortTransaction",
                                                 "Page_CommitTransaction" };
 
+               static readonly object [] EventKeys = {
+                                                Control.InitEvent,
+#if NET_2_0
+                                                Page.PreInitEvent,
+                                                Page.PreLoadEvent,
+                                                Page.LoadCompleteEvent,
+                                                Page.PreRenderCompleteEvent,
+                                                Page.SaveStateCompleteEvent,
+                                                Page.InitCompleteEvent,
+#endif
+                                               Control.LoadEvent,
+                                               Control.DataBindingEvent,
+                                               Control.PreRenderEvent,
+                                               Control.DisposedEvent,
+                                               Control.UnloadEvent,
+                                               error,
+                                               abortTransaction,
+                                               commitTransaction
+               };
+
+               enum LifeCycleEvent
+               {
+                       Init,
+#if NET_2_0
+                       PreInit,
+                       PreLoad,
+                       LoadComplete,
+                       PreRenderComplete,
+                       SaveStateComplete,
+                       InitComplete,
+#endif
+                       Load,
+                       DataBinding,
+                       PreRender,
+                       Disposed,
+                       Unload,
+                       Error,
+                       AbortTransaction,
+                       CommitTransaction
+               }
+
                const BindingFlags bflags = BindingFlags.Public |
                                                BindingFlags.NonPublic |
                                                BindingFlags.Instance;
 
-               private static string hashTableMutex = "lock"; //used to sync access ResourceHash property
-               private byte [] GetResourceBytes (Type type)
+               byte [] GetResourceBytes (Type type)
                {
                        Hashtable table = (Hashtable) AppDomain.CurrentDomain.GetData ("TemplateControl.RES_BYTES");
                        if (table == null) {
@@ -72,7 +113,7 @@ namespace System.Web.UI {
                        }
                        return (byte []) table [type];
                }
-               private void SetResourceBytes (Type type, byte [] bytes)
+               void SetResourceBytes (Type type, byte [] bytes)
                {
                        Hashtable table = (Hashtable) AppDomain.CurrentDomain.GetData ("TemplateControl.RES_BYTES");
                        if (table == null) {
@@ -83,7 +124,7 @@ namespace System.Web.UI {
                        return;
                }
 
-               private Hashtable ResourceHash
+               Hashtable ResourceHash
                {
                        get
                        {
@@ -94,23 +135,26 @@ namespace System.Web.UI {
                                }
                                return table;
                        }
+                       set
+                       {
+                               AppDomain.CurrentDomain.SetData ("TemplateControl.RES_STRING", value);
+                       }
                }
 
-               private string CachedString (string filename, int offset, int size)
+               string CachedString (Type type, int offset, int size)
                {
-                       string key = filename + offset + size;
-                       lock (hashTableMutex) {
-                               string strObj = (string) ResourceHash [key];
-                               if (strObj == null) {
+                       CacheKey key = new CacheKey (type, offset, size);
 
-                                       char [] tmp = System.Text.Encoding.UTF8.GetChars (GetResourceBytes (this.GetType ()), offset, size);
-                                       strObj = new string (tmp);
-                                       ResourceHash.Add (key, strObj);
-                               }
+                       string strObj = (string) ResourceHash [key];
+                       if (strObj == null) {
+                               char [] tmp = System.Text.Encoding.UTF8.GetChars (GetResourceBytes (this.GetType ()), offset, size);
+                               strObj = new string (tmp);
 
-                               return strObj;
+                               Hashtable tmpResourceHash = (Hashtable) ResourceHash.Clone ();
+                               tmpResourceHash.Add (key, strObj);
+                               ResourceHash = tmpResourceHash;
                        }
-
+                       return strObj;
                }
                public virtual string TemplateSourceDirectory_Private
                {
@@ -152,55 +196,169 @@ namespace System.Web.UI {
                                                                                        int size,
                                                                                        bool fAsciiOnly)
                {
-                       string str = CachedString (this.GetType ().FullName, offset, size);
+                       string str = CachedString (this.GetType (), offset, size);
                        return new LiteralControl (str);
                }
 
-               internal void WireupAutomaticEvents ()
+               sealed class EventMethodMap
                {
-                       if (!SupportAutoEvents || !AutoEventWireup)
-                               return;
+                       public EventMethodMap (LifeCycleEvent EventKeyIndex, MethodInfo Method, bool NoParameters)
+                       {
+                               this.EventKeyIndex = EventKeyIndex;
+                               this.Method = Method;
+                               this.NoParameters = NoParameters;
+                       }
 
-                       Type type = GetType ();
-                       foreach (string methodName in methodNames) {
-                               MethodInfo method = type.GetMethod (methodName, bflags);
-                               if (method == null)
-                                       continue;
+                       public readonly LifeCycleEvent EventKeyIndex;
+                       public readonly MethodInfo Method;
+                       public readonly bool NoParameters;
+               }
+
+               // This hashtable cashes methods and events found in user code
+               const string eventMethodCacheKey = "eventMethodCacheKey";
+               static Hashtable EventMethodCache
+               {
+                       get { return (Hashtable) AppDomain.CurrentDomain.GetData (eventMethodCacheKey); }
+                       set { AppDomain.CurrentDomain.SetData (eventMethodCacheKey, value); }
+               }
 
-#if ONLY_1_1
-                               if (method.DeclaringType != type) {
-                                       if (!method.IsPublic && !method.IsFamilyOrAssembly &&
-                                           !method.IsFamilyAndAssembly && !method.IsFamily)
+               internal void WireupAutomaticEvents ()
+               {
+                       Type cacheKey = this.GetType ();
+                       Hashtable eventMethodCache = EventMethodCache;
+                       ArrayList eventMethodList = eventMethodCache == null ? null : (ArrayList) eventMethodCache [cacheKey];
+
+                       if (eventMethodList == null) {
+                               eventMethodList = new ArrayList ();
+
+                               if (!SupportAutoEvents || !AutoEventWireup)
+                                       return;
+
+                               Type thisType = typeof (TemplateControl);
+                               Type voidType = typeof (void);
+                               Type [] DefaultParams = new Type [] {
+                               typeof (object),
+                               typeof (EventArgs) };
+
+                LifeCycleEvent[] _pageEvents = new LifeCycleEvent[] { 
+                    LifeCycleEvent.PreInit,
+                    LifeCycleEvent.PreLoad,
+                    LifeCycleEvent.LoadComplete,
+                    LifeCycleEvent.PreRenderComplete,
+                    LifeCycleEvent.SaveStateComplete,
+                    LifeCycleEvent.InitComplete
+                };
+                List<LifeCycleEvent> pageEvents = new List<LifeCycleEvent>(_pageEvents);
+
+                bool isPage = Page.GetType().IsAssignableFrom(GetType());
+
+                               for (int i = 0; i < methodNames.Length; i++) {
+                    
+                    // Don't look for page-only events in non-page controls.
+                    if (!isPage && pageEvents.Contains((LifeCycleEvent)i))
+                        continue;
+
+                                       string methodName = methodNames [i];
+                                       MethodInfo method;
+                                       bool noParams = false;
+                                       Type type = GetType ();
+                                       do {
+                                               method = type.GetMethod (methodName, bflags, null, DefaultParams, null);
+                                               if (method != null) {
+                                                       break;
+                                               }
+
+                                               type = type.BaseType;
+                                       }
+                                       while (type != thisType);
+
+                                       if (method == null) {
+                                               type = GetType ();
+                                               do {
+                                                       method = type.GetMethod (methodName, bflags, null, Type.EmptyTypes, null);
+                                                       if (method != null) {
+                                                               noParams = true;
+                                                               break;
+                                                       }
+
+                                                       type = type.BaseType;
+                                               }
+                                               while (type != thisType);
+
+                                               if (method == null)
+                                                       continue;
+                                       }
+                                       if (method.ReturnType != voidType)
                                                continue;
+
+                                       eventMethodList.Add (new EventMethodMap ((LifeCycleEvent) i, method, noParams));
                                }
-#endif
+                               // We copy to not lock
 
-                               if (method.ReturnType != typeof (void))
-                                       continue;
+                               Hashtable newEventMethodCache = eventMethodCache == null ? new Hashtable () : (Hashtable) eventMethodCache.Clone ();
+                               newEventMethodCache [cacheKey] = eventMethodList;
+                               EventMethodCache = newEventMethodCache;
+                       }
 
-                               ParameterInfo [] parms = method.GetParameters ();
-                               int length = parms.Length;
-                               bool noParams = (length == 0);
-                               if (!noParams && (length != 2 ||
-                                       parms [0].ParameterType != typeof (object) ||
-                                       parms [1].ParameterType != typeof (EventArgs)))
-                                       continue;
+                       foreach (EventMethodMap eventMethod in eventMethodList) {
+                               EventHandler handler = eventMethod.NoParameters ?
+                                       new NoParamsInvoker (this, eventMethod.Method).FakeDelegate :
+                                       (EventHandler)Delegate.CreateDelegate (typeof (EventHandler), this, eventMethod.Method);
 
-                               int pos = methodName.IndexOf ("_");
-                               string eventName = methodName.Substring (pos + 1);
-                               EventInfo evt = type.GetEvent (eventName);
-                               if (evt == null) {
-                                       /* This should never happen */
+                               object eventKey = EventKeys [(int) eventMethod.EventKeyIndex];
+
+                               Delegate existing = Events [eventKey];
+                               if (existing != null && handler.Equals(existing))
                                        continue;
-                               }
 
-                               if (noParams) {
-                                       NoParamsInvoker npi = new NoParamsInvoker (this, methodName);
-                                       evt.AddEventHandler (this, npi.FakeDelegate);
-                               }
-                               else {
-                                       evt.AddEventHandler (this, Delegate.CreateDelegate (
-                                                       typeof (EventHandler), this, methodName));
+                               switch (eventMethod.EventKeyIndex) {
+                               case LifeCycleEvent.Init:
+                                       Init += handler;
+                                       break;
+#if NET_2_0
+                               case LifeCycleEvent.PreInit:
+                                       ((Page)this).PreInit += handler;
+                                       break;
+                               case LifeCycleEvent.PreLoad:
+                                       ((Page) this).PreLoad += handler;
+                                       break;
+                               case LifeCycleEvent.LoadComplete:
+                                       ((Page) this).LoadComplete += handler;
+                                       break;
+                               case LifeCycleEvent.PreRenderComplete:
+                                       ((Page) this).PreRenderComplete += handler;
+                                       break;
+                               case LifeCycleEvent.SaveStateComplete:
+                                       ((Page) this).SaveStateComplete += handler;
+                                       break;
+                               case LifeCycleEvent.InitComplete:
+                                       ((Page) this).InitComplete += handler;
+                                       break;
+#endif
+                               case LifeCycleEvent.Load:
+                                       Load += handler;
+                                       break;
+                               case LifeCycleEvent.DataBinding:
+                                       DataBinding += handler;
+                                       break;
+                               case LifeCycleEvent.PreRender:
+                                       PreRender += handler;
+                                       break;
+                               case LifeCycleEvent.Disposed:
+                                       Disposed += handler;
+                                       break;
+                               case LifeCycleEvent.Unload:
+                                       Unload += handler;
+                                       break;
+                               case LifeCycleEvent.Error:
+                                       Error += handler;
+                                       break;
+                               case LifeCycleEvent.AbortTransaction:
+                                       AbortTransaction += handler;
+                                       break;
+                               case LifeCycleEvent.CommitTransaction:
+                                       CommitTransaction += handler;
+                                       break;
                                }
                        }
                }
@@ -216,12 +374,35 @@ namespace System.Web.UI {
                                throw new ArgumentNullException ("virtualPath");
 
                        string vpath = UrlUtils.Combine (TemplateSourceDirectory, virtualPath);
-                       return PageMapper.GetObjectType (vpath);
+                       return PageMapper.GetObjectType (Context, vpath);
                }
 
                public Control LoadControl (string virtualPath)
                {
-                       object control = Activator.CreateInstance (GetTypeFromControlPath (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);
+                       return LoadControl (type, null);
+               }
+
+               public Control LoadControl (Type type, object [] parameters)
+               {
+                       object [] attrs = type.GetCustomAttributes (typeof (PartialCachingAttribute), true);
+                       if (attrs != null && attrs.Length == 1) {
+                               PartialCachingAttribute attr = (PartialCachingAttribute) attrs [0];
+                               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, parameters);
                        if (control is UserControl)
                                ((UserControl) control).InitializeAsUserControl (Page);
 
@@ -292,7 +473,9 @@ namespace System.Web.UI {
                        java.lang.ClassLoader contextClassLoader = c.getClassLoader ();
 
                        //TODO:move this code to page mapper
-                       string assemblyName = PageMapper.GetAssemblyResource (this.AppRelativeVirtualPath);
+                       string assemblyName = PageMapper.GetAssemblyResource (Context, VirtualPathUtility.ToAbsolute (AppRelativeVirtualPath));
+                       if (assemblyName == null)
+                               throw new HttpException (404, "The requested resource (" + this.AppRelativeVirtualPath + ") is not available.");
 
                        java.io.InputStream inputStream = contextClassLoader.getResourceAsStream (assemblyName);
 
@@ -333,7 +516,7 @@ namespace System.Web.UI {
                protected void WriteUTF8ResourceString (HtmlTextWriter output, int offset,
                                                        int size, bool fAsciiOnly)
                {
-                       string str = CachedString (this.GetType ().FullName, offset, size);
+                       string str = CachedString (this.GetType (), offset, size);
                        output.Write (str);
                }
 
@@ -381,6 +564,34 @@ namespace System.Web.UI {
                        }
                }
 
+               sealed class CacheKey
+               {
+                       readonly Type _type;
+                       readonly int _offset;
+                       readonly int _size;
+
+                       public CacheKey (Type type, int offset, int size)
+                       {
+                               _type = type;
+                               _offset = offset;
+                               _size = size;
+                       }
+
+                       public override int GetHashCode ()
+                       {
+                               return _type.GetHashCode () ^ _offset ^ _size;
+                       }
+
+                       public override bool Equals (object obj)
+                       {
+                               if (obj == null || !(obj is CacheKey))
+                                       return false;
+
+                               CacheKey key = (CacheKey) obj;
+                               return key._type == _type && key._offset == _offset && key._size == _size;
+                       }
+               }
+
 #if NET_2_0
 
                string _appRelativeVirtualPath = null;
@@ -401,6 +612,10 @@ namespace System.Web.UI {
                        }
                }
 
+               internal override TemplateControl TemplateControlInternal {
+                       get { return this; }
+               }
+
                protected internal object Eval (string expression)
                {
                        return DataBinder.Eval (Page.GetDataItem (), expression);
@@ -446,11 +661,9 @@ namespace System.Web.UI {
                        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 ();
+                       return ConvertResource (GetGlobalResourceObject (className, resourceKey), objType, propName);
                }
 
                protected Object GetLocalResourceObject (string resourceKey)
@@ -458,11 +671,26 @@ namespace System.Web.UI {
                        return HttpContext.GetLocalResourceObject (Context.Request.Path, resourceKey);
                }
 
-               [MonoTODO ("Not implemented")]
                protected Object GetLocalResourceObject (string resourceKey, Type objType, string propName)
                {
-                       // FIXME: not sure how to implement that one yet
-                       throw new NotSupportedException ();
+                       return ConvertResource (GetLocalResourceObject (resourceKey), objType, propName);
+               }
+
+               static Object ConvertResource (Object resource, Type objType, string propName) {
+                       if (resource == null)
+                               return resource;
+
+                       PropertyDescriptor pdesc = TypeDescriptor.GetProperties (objType) [propName];
+                       if (pdesc == null)
+                               return resource;
+
+                       TypeConverter converter = pdesc.Converter;
+                       if (converter == null)
+                               return resource;
+
+                       return resource is string ?
+                               converter.ConvertFromInvariantString ((string) resource) :
+                               converter.ConvertFrom (resource);
                }
 
 #endif