* TemplateControl.jvm.cs: perforamce optimization in GetLocalResourceObject
[mono.git] / mcs / class / System.Web / System.Web.UI / TemplateControl.jvm.cs
1 //
2 // (C) 2005 Mainsoft Corporation (http://www.mainsoft.com)
3 //
4
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //
25
26 using System;
27 using System.Collections;
28 using System.ComponentModel;
29 using System.Reflection;
30 using System.Web;
31 using System.IO;
32 using System.Web.J2EE;
33 using System.Xml;
34 using vmw.common;
35 using System.Web.Util;
36 using System.Threading;
37
38 namespace System.Web.UI {
39
40         public abstract class TemplateControl : Control, INamingContainer
41         {
42                 static readonly object abortTransaction = new object ();
43                 static readonly object commitTransaction = new object ();
44                 static readonly object error = new object ();
45                 static readonly string [] methodNames = { "Page_Init",
46 #if NET_2_0
47                                                  "Page_PreInit",
48                                                  "Page_PreLoad",
49                                                  "Page_LoadComplete",
50                                                  "Page_PreRenderComplete",
51                                                  "Page_SaveStateComplete",
52                                                  "Page_InitComplete",
53 #endif
54                                                  "Page_Load",
55                                                  "Page_DataBind",
56                                                  "Page_PreRender",
57                                                  "Page_Disposed",
58                                                  "Page_Unload",
59                                                  "Page_Error",
60                                                  "Page_AbortTransaction",
61                                                  "Page_CommitTransaction" };
62
63                 static readonly object [] EventKeys = {
64                                                  Control.InitEvent,
65 #if NET_2_0
66                                                  Page.PreInitEvent,
67                                                  Page.PreLoadEvent,
68                                                  Page.LoadCompleteEvent,
69                                                  Page.PreRenderCompleteEvent,
70                                                  Page.SaveStateCompleteEvent,
71                                                  Page.InitCompleteEvent,
72 #endif
73                                                 Control.LoadEvent,
74                                                 Control.DataBindingEvent,
75                                                 Control.PreRenderEvent,
76                                                 Control.DisposedEvent,
77                                                 Control.UnloadEvent,
78                                                 error,
79                                                 abortTransaction,
80                                                 commitTransaction
81                 };
82
83                 enum LifeCycleEvent
84                 {
85                         Init,
86 #if NET_2_0
87                         PreInit,
88                         PreLoad,
89                         LoadComplete,
90                         PreRenderComplete,
91                         SaveStateComplete,
92                         InitComplete,
93 #endif
94                         Load,
95                         DataBinding,
96                         PreRender,
97                         Disposed,
98                         Unload,
99                         Error,
100                         AbortTransaction,
101                         CommitTransaction
102                 }
103
104                 const BindingFlags bflags = BindingFlags.Public |
105                                                 BindingFlags.NonPublic |
106                                                 BindingFlags.Instance;
107
108                 private byte [] GetResourceBytes (Type type)
109                 {
110                         Hashtable table = (Hashtable) AppDomain.CurrentDomain.GetData ("TemplateControl.RES_BYTES");
111                         if (table == null) {
112                                 return null;
113                         }
114                         return (byte []) table [type];
115                 }
116                 private void SetResourceBytes (Type type, byte [] bytes)
117                 {
118                         Hashtable table = (Hashtable) AppDomain.CurrentDomain.GetData ("TemplateControl.RES_BYTES");
119                         if (table == null) {
120                                 table = new Hashtable ();
121                                 AppDomain.CurrentDomain.SetData ("TemplateControl.RES_BYTES", table);
122                         }
123                         table [type] = bytes;
124                         return;
125                 }
126
127                 private Hashtable ResourceHash
128                 {
129                         get
130                         {
131                                 Hashtable table = (Hashtable) AppDomain.CurrentDomain.GetData ("TemplateControl.RES_STRING");
132                                 if (table == null) {
133                                         table = new Hashtable ();
134                                         AppDomain.CurrentDomain.SetData ("TemplateControl.RES_STRING", table);
135                                 }
136                                 return table;
137                         }
138                         set
139                         {
140                                 AppDomain.CurrentDomain.SetData ("TemplateControl.RES_STRING", value);
141                         }
142                 }
143
144                 private string CachedString (Type type, int offset, int size)
145                 {
146                         CacheKey key = new CacheKey (type, offset, size);
147
148                         string strObj = (string) ResourceHash [key];
149                         if (strObj == null) {
150                                 char [] tmp = System.Text.Encoding.UTF8.GetChars (GetResourceBytes (this.GetType ()), offset, size);
151                                 strObj = new string (tmp);
152
153                                 Hashtable tmpResourceHash = (Hashtable) ResourceHash.Clone ();
154                                 tmpResourceHash.Add (key, strObj);
155                                 ResourceHash = tmpResourceHash;
156                         }
157                         return strObj;
158                 }
159                 public virtual string TemplateSourceDirectory_Private
160                 {
161                         get { return null; }
162                 }
163
164                 #region Constructor
165                 protected TemplateControl ()
166                 {
167                         Construct ();
168                 }
169
170                 #endregion
171
172                 #region Properties
173                 [EditorBrowsable (EditorBrowsableState.Never)]
174                 protected virtual int AutoHandlers
175                 {
176                         get { return 0; }
177                         set { }
178                 }
179
180                 [EditorBrowsable (EditorBrowsableState.Never)]
181                 protected virtual bool SupportAutoEvents
182                 {
183                         get { return true; }
184                 }
185
186                 #endregion
187
188                 #region Methods
189
190                 protected virtual void Construct ()
191                 {
192                 }
193
194                 [MonoTODO]
195                 protected LiteralControl CreateResourceBasedLiteralControl (int offset,
196                                                                                         int size,
197                                                                                         bool fAsciiOnly)
198                 {
199                         string str = CachedString (this.GetType (), offset, size);
200                         return new LiteralControl (str);
201                 }
202
203                 sealed class EventMethodMap
204                 {
205                         public EventMethodMap (LifeCycleEvent EventKeyIndex, MethodInfo Method, bool NoParameters)
206                         {
207                                 this.EventKeyIndex = EventKeyIndex;
208                                 this.Method = Method;
209                                 this.NoParameters = NoParameters;
210                         }
211
212                         public readonly LifeCycleEvent EventKeyIndex;
213                         public readonly MethodInfo Method;
214                         public readonly bool NoParameters;
215                 }
216
217                 // This hashtable cashes methods and events found in user code
218                 const string eventMethodCacheKey = "eventMethodCacheKey";
219                 static Hashtable EventMethodCache
220                 {
221                         get { return (Hashtable) AppDomain.CurrentDomain.GetData (eventMethodCacheKey); }
222                         set { AppDomain.CurrentDomain.SetData (eventMethodCacheKey, value); }
223                 }
224
225                 internal void WireupAutomaticEvents ()
226                 {
227                         Type cacheKey = this.GetType ();
228                         Hashtable eventMethodCache = EventMethodCache;
229                         ArrayList eventMethodList = eventMethodCache == null ? null : (ArrayList) eventMethodCache [cacheKey];
230
231                         if (eventMethodList == null) {
232                                 eventMethodList = new ArrayList ();
233
234                                 if (!SupportAutoEvents || !AutoEventWireup)
235                                         return;
236
237                                 Type thisType = typeof (TemplateControl);
238                                 Type voidType = typeof (void);
239                                 Type [] DefaultParams = new Type [] {
240                                 typeof (object),
241                                 typeof (EventArgs) };
242
243                                 for (int i = 0; i < methodNames.Length; i++) {
244                                         string methodName = methodNames [i];
245                                         MethodInfo method;
246                                         bool noParams = false;
247                                         Type type = GetType ();
248                                         do {
249                                                 method = type.GetMethod (methodName, bflags, null, DefaultParams, null);
250                                                 if (method != null) {
251                                                         break;
252                                                 }
253
254                                                 type = type.BaseType;
255                                         }
256                                         while (type != thisType);
257
258                                         if (method == null) {
259                                                 type = GetType ();
260                                                 do {
261                                                         method = type.GetMethod (methodName, bflags, null, Type.EmptyTypes, null);
262                                                         if (method != null) {
263                                                                 noParams = true;
264                                                                 break;
265                                                         }
266
267                                                         type = type.BaseType;
268                                                 }
269                                                 while (type != thisType);
270
271                                                 if (method == null)
272                                                         continue;
273                                         }
274                                         if (method.ReturnType != voidType)
275                                                 continue;
276
277                                         eventMethodList.Add (new EventMethodMap ((LifeCycleEvent) i, method, noParams));
278                                 }
279                                 // We copy to not lock
280
281                                 Hashtable newEventMethodCache = eventMethodCache == null ? new Hashtable () : (Hashtable) eventMethodCache.Clone ();
282                                 newEventMethodCache [cacheKey] = eventMethodList;
283                                 EventMethodCache = newEventMethodCache;
284                         }
285
286                         foreach (EventMethodMap eventMethod in eventMethodList) {
287                                 EventHandler handler = eventMethod.NoParameters ?
288                                         new NoParamsInvoker (this, eventMethod.Method).FakeDelegate :
289                                         (EventHandler)Delegate.CreateDelegate (typeof (EventHandler), this, eventMethod.Method);
290
291                                 object eventKey = EventKeys [(int) eventMethod.EventKeyIndex];
292
293                                 Delegate existing = Events [eventKey];
294                                 if (existing != null && handler.Equals(existing))
295                                         continue;
296
297                                 switch (eventMethod.EventKeyIndex) {
298                                 case LifeCycleEvent.Init:
299                                         Init += handler;
300                                         break;
301 #if NET_2_0
302                                 case LifeCycleEvent.PreInit:
303                                         ((Page)this).PreInit += handler;
304                                         break;
305                                 case LifeCycleEvent.PreLoad:
306                                         ((Page) this).PreLoad += handler;
307                                         break;
308                                 case LifeCycleEvent.LoadComplete:
309                                         ((Page) this).LoadComplete += handler;
310                                         break;
311                                 case LifeCycleEvent.PreRenderComplete:
312                                         ((Page) this).PreRenderComplete += handler;
313                                         break;
314                                 case LifeCycleEvent.SaveStateComplete:
315                                         ((Page) this).SaveStateComplete += handler;
316                                         break;
317                                 case LifeCycleEvent.InitComplete:
318                                         ((Page) this).InitComplete += handler;
319                                         break;
320 #endif
321                                 case LifeCycleEvent.Load:
322                                         Load += handler;
323                                         break;
324                                 case LifeCycleEvent.DataBinding:
325                                         DataBinding += handler;
326                                         break;
327                                 case LifeCycleEvent.PreRender:
328                                         PreRender += handler;
329                                         break;
330                                 case LifeCycleEvent.Disposed:
331                                         Disposed += handler;
332                                         break;
333                                 case LifeCycleEvent.Unload:
334                                         Unload += handler;
335                                         break;
336                                 case LifeCycleEvent.Error:
337                                         Error += handler;
338                                         break;
339                                 case LifeCycleEvent.AbortTransaction:
340                                         AbortTransaction += handler;
341                                         break;
342                                 case LifeCycleEvent.CommitTransaction:
343                                         CommitTransaction += handler;
344                                         break;
345                                 }
346                         }
347                 }
348
349                 [EditorBrowsable (EditorBrowsableState.Never)]
350                 protected virtual void FrameworkInitialize ()
351                 {
352                 }
353
354                 Type GetTypeFromControlPath (string virtualPath)
355                 {
356                         if (virtualPath == null)
357                                 throw new ArgumentNullException ("virtualPath");
358
359                         string vpath = UrlUtils.Combine (TemplateSourceDirectory, virtualPath);
360                         return PageMapper.GetObjectType (Context, vpath);
361                 }
362
363                 public Control LoadControl (string virtualPath)
364                 {
365 #if NET_2_0
366                         if (virtualPath == null)
367                                 throw new ArgumentNullException ("virtualPath");
368 #else
369                         if (virtualPath == null)
370                                 throw new HttpException ("virtualPath is null");
371 #endif
372                         Type type = GetTypeFromControlPath (virtualPath);
373                         return LoadControl (type, null);
374                 }
375
376                 public Control LoadControl (Type type, object [] parameters)
377                 {
378                         object [] attrs = type.GetCustomAttributes (typeof (PartialCachingAttribute), true);
379                         if (attrs != null && attrs.Length == 1) {
380                                 PartialCachingAttribute attr = (PartialCachingAttribute) attrs [0];
381                                 PartialCachingControl ctrl = new PartialCachingControl (type, parameters);
382                                 ctrl.VaryByParams = attr.VaryByParams;
383                                 ctrl.VaryByControls = attr.VaryByControls;
384                                 ctrl.VaryByCustom = attr.VaryByCustom;
385                                 return ctrl;
386                         }
387
388                         object control = Activator.CreateInstance (type, parameters);
389                         if (control is UserControl)
390                                 ((UserControl) control).InitializeAsUserControl (Page);
391
392                         return (Control) control;
393                 }
394
395                 public ITemplate LoadTemplate (string virtualPath)
396                 {
397                         Type t = GetTypeFromControlPath (virtualPath);
398                         return new SimpleTemplate (t);
399                 }
400
401                 protected virtual void OnAbortTransaction (EventArgs e)
402                 {
403                         EventHandler eh = Events [abortTransaction] as EventHandler;
404                         if (eh != null)
405                                 eh (this, e);
406                 }
407
408                 protected virtual void OnCommitTransaction (EventArgs e)
409                 {
410                         EventHandler eh = Events [commitTransaction] as EventHandler;
411                         if (eh != null)
412                                 eh (this, e);
413                 }
414
415                 protected virtual void OnError (EventArgs e)
416                 {
417                         EventHandler eh = Events [error] as EventHandler;
418                         if (eh != null)
419                                 eh (this, e);
420                 }
421
422                 [MonoNotSupported ("Not supported")]
423                 public Control ParseControl (string content)
424                 {
425                         throw new NotSupportedException ();
426                 }
427
428                 [MonoLimitation ("Always returns false")]
429                 public virtual bool TestDeviceFilter (string filterName)
430                 {
431                         return false;
432                 }
433
434                 [MonoTODO]
435                 [EditorBrowsable (EditorBrowsableState.Never)]
436                 public static object ReadStringResource (Type t)
437                 {
438                         return t;
439                 }
440 #if NET_2_0
441                 [MonoTODO ("is this correct?")]
442                 public Object ReadStringResource ()
443                 {
444                         return this.GetType ();
445                 }
446 #endif
447                 [MonoTODO]
448                 [EditorBrowsable (EditorBrowsableState.Never)]
449                 protected void SetStringResourcePointer (object stringResourcePointer,
450                                                          int maxResourceOffset)
451                 {
452                         if (GetResourceBytes (this.GetType ()) != null)
453                                 return;
454
455                         java.lang.Class c = vmw.common.TypeUtils.ToClass (stringResourcePointer);
456                         java.lang.ClassLoader contextClassLoader = c.getClassLoader ();
457
458                         //TODO:move this code to page mapper
459                         string assemblyName = PageMapper.GetAssemblyResource (Context, VirtualPathUtility.ToAbsolute (AppRelativeVirtualPath));
460                         if (assemblyName == null)
461                                 throw new HttpException (404, "The requested resource (" + this.AppRelativeVirtualPath + ") is not available.");
462
463                         java.io.InputStream inputStream = contextClassLoader.getResourceAsStream (assemblyName);
464
465                         System.IO.Stream strim = null;
466                         if (inputStream == null) {
467                                 string descPath = String.Join ("/", new string [] { "assemblies", this.GetType ().Assembly.GetName ().Name, assemblyName });
468                                 try {
469                                         strim = new StreamReader (HttpContext.Current.Request.MapPath ("/" + descPath)).BaseStream;
470                                 }
471                                 catch (Exception ex) {
472                                         throw new System.IO.IOException ("couldn't open resource file:" + assemblyName, ex);
473                                 }
474                                 if (strim == null)
475                                         throw new System.IO.IOException ("couldn't open resource file:" + assemblyName);
476                         }
477
478                         try {
479                                 if (strim == null)
480                                         strim = (System.IO.Stream) vmw.common.IOUtils.getStream (inputStream);
481                                 int capacity = (int) strim.Length;
482                                 byte [] resourceBytes = new byte [capacity];
483                                 strim.Read (resourceBytes, 0, capacity);
484                                 SetResourceBytes (this.GetType (), resourceBytes);
485                         }
486                         catch (Exception e) {
487                                 throw new HttpException ("problem with dll.ghres file", e);
488                         }
489                         finally {
490                                 if (strim != null)
491                                         strim.Close ();
492                                 if (inputStream != null)
493                                         inputStream.close ();
494                         }
495                 }
496
497                 [MonoTODO]
498                 [EditorBrowsable (EditorBrowsableState.Never)]
499                 protected void WriteUTF8ResourceString (HtmlTextWriter output, int offset,
500                                                         int size, bool fAsciiOnly)
501                 {
502                         string str = CachedString (this.GetType (), offset, size);
503                         output.Write (str);
504                 }
505
506                 #endregion
507
508                 #region Events
509
510                 [WebSysDescription ("Raised when the user aborts a transaction.")]
511                 public event EventHandler AbortTransaction
512                 {
513                         add { Events.AddHandler (abortTransaction, value); }
514                         remove { Events.RemoveHandler (abortTransaction, value); }
515                 }
516
517                 [WebSysDescription ("Raised when the user initiates a transaction.")]
518                 public event EventHandler CommitTransaction
519                 {
520                         add { Events.AddHandler (commitTransaction, value); }
521                         remove { Events.RemoveHandler (commitTransaction, value); }
522                 }
523
524                 [WebSysDescription ("Raised when an exception occurs that cannot be handled.")]
525                 public event EventHandler Error
526                 {
527                         add { Events.AddHandler (error, value); }
528                         remove { Events.RemoveHandler (error, value); }
529                 }
530
531                 #endregion
532
533                 class SimpleTemplate : ITemplate
534                 {
535                         Type type;
536
537                         public SimpleTemplate (Type type)
538                         {
539                                 this.type = type;
540                         }
541
542                         public void InstantiateIn (Control control)
543                         {
544                                 Control template = Activator.CreateInstance (type) as Control;
545                                 template.SetBindingContainer (false);
546                                 control.Controls.Add (template);
547                         }
548                 }
549
550                 sealed private class CacheKey
551                 {
552                         readonly Type _type;
553                         readonly int _offset;
554                         readonly int _size;
555
556                         public CacheKey (Type type, int offset, int size)
557                         {
558                                 _type = type;
559                                 _offset = offset;
560                                 _size = size;
561                         }
562
563                         public override int GetHashCode ()
564                         {
565                                 return _type.GetHashCode () ^ _offset ^ _size;
566                         }
567
568                         public override bool Equals (object obj)
569                         {
570                                 if (obj == null || !(obj is CacheKey))
571                                         return false;
572
573                                 CacheKey key = (CacheKey) obj;
574                                 return key._type == _type && key._offset == _offset && key._size == _size;
575                         }
576                 }
577
578 #if NET_2_0
579
580                 Assembly _resourceAssembly = null;
581                 string _appRelativeVirtualPath = null;
582
583                 public string AppRelativeVirtualPath
584                 {
585                         get { return _appRelativeVirtualPath; }
586                         set
587                         {
588                                 if (value == null)
589                                         throw new ArgumentNullException ("value");
590                                 if (!UrlUtils.IsRooted (value) && !(value.Length > 0 && value [0] == '~'))
591                                         throw new ArgumentException ("The path that is set is not rooted");
592                                 _appRelativeVirtualPath = value;
593
594                                 int lastSlash = _appRelativeVirtualPath.LastIndexOf ('/');
595                                 AppRelativeTemplateSourceDirectory = (lastSlash > 0) ? _appRelativeVirtualPath.Substring (0, lastSlash + 1) : "~/";
596                         }
597                 }
598
599                 internal override TemplateControl TemplateControlInternal {
600                         get { return this; }
601                 }
602
603                 protected internal object Eval (string expression)
604                 {
605                         return DataBinder.Eval (Page.GetDataItem (), expression);
606                 }
607
608                 protected internal string Eval (string expression, string format)
609                 {
610                         return DataBinder.Eval (Page.GetDataItem (), expression, format);
611                 }
612
613                 protected internal object XPath (string xpathexpression)
614                 {
615                         return XPathBinder.Eval (Page.GetDataItem (), xpathexpression);
616                 }
617
618                 protected internal object XPath (string xpathexpression, IXmlNamespaceResolver resolver)
619                 {
620                         return XPathBinder.Eval (Page.GetDataItem (), xpathexpression, null, resolver);
621                 }
622
623                 protected internal string XPath (string xpathexpression, string format)
624                 {
625                         return XPathBinder.Eval (Page.GetDataItem (), xpathexpression, format);
626                 }
627
628                 protected internal string XPath (string xpathexpression, string format, IXmlNamespaceResolver resolver)
629                 {
630                         return XPathBinder.Eval (Page.GetDataItem (), xpathexpression, format, resolver);
631                 }
632
633                 protected internal IEnumerable XPathSelect (string xpathexpression)
634                 {
635                         return XPathBinder.Select (Page.GetDataItem (), xpathexpression);
636                 }
637
638                 protected internal IEnumerable XPathSelect (string xpathexpression, IXmlNamespaceResolver resolver)
639                 {
640                         return XPathBinder.Select (Page.GetDataItem (), xpathexpression, resolver);
641                 }
642
643                 protected object GetGlobalResourceObject (string className, string resourceKey)
644                 {
645                         return HttpContext.GetGlobalResourceObject (className, resourceKey);
646                 }
647
648                 protected object GetGlobalResourceObject (string className, string resourceKey, Type objType, string propName)
649                 {
650                         return ConvertResource (GetGlobalResourceObject (className, resourceKey), objType, propName);
651                 }
652
653                 protected Object GetLocalResourceObject (string resourceKey)
654                 {
655                         if (_resourceAssembly == null)
656                                 _resourceAssembly = System.Web.Compilation.AppResourcesCompiler.GetCachedLocalResourcesAssembly (TemplateSourceDirectory);
657
658                         string fileName = string.Empty;
659                         int lastSlash = AppRelativeVirtualPath.LastIndexOf ('/');
660
661                         if (lastSlash > 0 && AppRelativeVirtualPath.Length > lastSlash + 1)
662                                 fileName = AppRelativeVirtualPath.Substring (lastSlash + 1);
663
664                         return HttpContext.GetResourceObject (fileName, resourceKey, Thread.CurrentThread.CurrentUICulture, _resourceAssembly);
665                 }
666
667                 protected Object GetLocalResourceObject (string resourceKey, Type objType, string propName)
668                 {
669                         return ConvertResource (GetLocalResourceObject (resourceKey), objType, propName);
670                 }
671
672                 static Object ConvertResource (Object resource, Type objType, string propName) {
673                         if (resource == null)
674                                 return resource;
675
676                         PropertyDescriptor pdesc = TypeDescriptor.GetProperties (objType) [propName];
677                         if (pdesc == null)
678                                 return resource;
679
680                         TypeConverter converter = pdesc.Converter;
681                         if (converter == null)
682                                 return resource;
683
684                         return resource is string ?
685                                 converter.ConvertFromInvariantString ((string) resource) :
686                                 converter.ConvertFrom (resource);
687                 }
688
689 #endif
690
691         }
692 }