Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateControl.jvm.cs
index 78c8754269b9c0b5591b2c8c88337700082b0eb8..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,18 +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;
-               static readonly Type [] NoParams = new Type [0];
 
-               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) {
@@ -73,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) {
@@ -84,7 +124,7 @@ namespace System.Web.UI {
                        return;
                }
 
-               private Hashtable ResourceHash
+               Hashtable ResourceHash
                {
                        get
                        {
@@ -95,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
                {
@@ -153,20 +196,20 @@ 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);
                }
 
                sealed class EventMethodMap
                {
-                       public EventMethodMap (EventInfo Event, MethodInfo Method, bool NoParameters)
+                       public EventMethodMap (LifeCycleEvent EventKeyIndex, MethodInfo Method, bool NoParameters)
                        {
-                               this.Event = Event;
+                               this.EventKeyIndex = EventKeyIndex;
                                this.Method = Method;
                                this.NoParameters = NoParameters;
                        }
 
-                       public readonly EventInfo Event;
+                       public readonly LifeCycleEvent EventKeyIndex;
                        public readonly MethodInfo Method;
                        public readonly bool NoParameters;
                }
@@ -197,7 +240,25 @@ namespace System.Web.UI {
                                typeof (object),
                                typeof (EventArgs) };
 
-                               foreach (string methodName in methodNames) {
+                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 ();
@@ -214,7 +275,7 @@ namespace System.Web.UI {
                                        if (method == null) {
                                                type = GetType ();
                                                do {
-                                                       method = type.GetMethod (methodName, bflags, null, NoParams, null);
+                                                       method = type.GetMethod (methodName, bflags, null, Type.EmptyTypes, null);
                                                        if (method != null) {
                                                                noParams = true;
                                                                break;
@@ -230,22 +291,7 @@ namespace System.Web.UI {
                                        if (method.ReturnType != voidType)
                                                continue;
 
-                                       int pos = methodName.IndexOf ("_");
-                                       string eventName = methodName.Substring (pos + 1);
-                                       EventInfo evt = GetType ().GetEvent (eventName);
-                                       if (evt == null) {
-                                               /* This should never happen */
-                                               continue;
-                                       }
-
-                                       eventMethodList.Add (new EventMethodMap (evt, method, noParams));
-#if ONLY_1_1
-                               if (method.DeclaringType != type) {
-                                       if (!method.IsPublic && !method.IsFamilyOrAssembly &&
-                                           !method.IsFamilyAndAssembly && !method.IsFamily)
-                                               continue;
-                               }
-#endif
+                                       eventMethodList.Add (new EventMethodMap ((LifeCycleEvent) i, method, noParams));
                                }
                                // We copy to not lock
 
@@ -255,12 +301,64 @@ namespace System.Web.UI {
                        }
 
                        foreach (EventMethodMap eventMethod in eventMethodList) {
-                               if (eventMethod.NoParameters) {
-                                       NoParamsInvoker npi = new NoParamsInvoker (this, eventMethod.Method);
-                                       eventMethod.Event.AddEventHandler (this, npi.FakeDelegate);
-                               }
-                               else {
-                                       eventMethod.Event.AddEventHandler (this, Delegate.CreateDelegate (typeof (EventHandler), this, eventMethod.Method));
+                               EventHandler handler = eventMethod.NoParameters ?
+                                       new NoParamsInvoker (this, eventMethod.Method).FakeDelegate :
+                                       (EventHandler)Delegate.CreateDelegate (typeof (EventHandler), this, eventMethod.Method);
+
+                               object eventKey = EventKeys [(int) eventMethod.EventKeyIndex];
+
+                               Delegate existing = Events [eventKey];
+                               if (existing != null && handler.Equals(existing))
+                                       continue;
+
+                               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;
                                }
                        }
                }
@@ -281,7 +379,30 @@ namespace System.Web.UI {
 
                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);
 
@@ -352,7 +473,7 @@ namespace System.Web.UI {
                        java.lang.ClassLoader contextClassLoader = c.getClassLoader ();
 
                        //TODO:move this code to page mapper
-                       string assemblyName = PageMapper.GetAssemblyResource (HttpContext.Current, 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.");
 
@@ -395,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);
                }
 
@@ -443,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;
@@ -463,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);
@@ -508,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)
@@ -520,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