Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / class / System.Web.Extensions / System.Web.UI / ScriptReferenceBase.cs
1 //
2 // Author:
3 //   Marek Habersack <grendel@twistedcode.net>
4 //
5 // (C) 2009-2011 Novell, Inc.  http://novell.com/
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 using System;
27 using System.Collections.Generic;
28 using System.Collections.Concurrent;
29 using System.IO;
30 using System.Text;
31 using System.ComponentModel;
32 using System.Reflection;
33 using System.Security.Permissions;
34 using System.Threading;
35 using System.Web.UI.WebControls;
36
37 namespace System.Web.UI
38 {
39         [AspNetHostingPermissionAttribute(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
40         [AspNetHostingPermissionAttribute(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41         public abstract class ScriptReferenceBase
42         {
43                 static SplitOrderedList <ResourceCacheEntry, bool> resourceCache;
44                 string _path;
45                 
46                 public bool NotifyScriptLoaded {
47                         get; set;
48                 }
49
50                 public string Path {
51                         get { return _path != null ? _path : String.Empty; }
52                         set { _path = value; }
53                 }
54                 
55                 [TypeConverterAttribute(typeof(StringArrayConverter))]
56                 public string[] ResourceUICultures {
57                         get; set;
58                 }
59                 
60                 public ScriptMode ScriptMode {
61                         get; set;
62                 }
63
64                 internal static Assembly ThisAssembly {
65                         get; private set;
66                 }
67                 
68                 static ScriptReferenceBase ()
69                 {
70                         ThisAssembly = typeof (ScriptReferenceBase).Assembly;
71                         resourceCache = new SplitOrderedList <ResourceCacheEntry, bool> (EqualityComparer <ResourceCacheEntry>.Default);
72                 }
73                 
74                 protected ScriptReferenceBase ()
75                 {
76                         this.NotifyScriptLoaded = true;
77                         this.ScriptMode = ScriptMode.Auto;
78                 }
79                 protected internal virtual bool IsAjaxFrameworkScript (ScriptManager scriptManager)
80                 {
81                         return false;
82                 }
83                 
84                 [Obsolete ("Use IsAjaxFrameworkScript(ScriptManager)")]
85                 protected internal abstract bool IsFromSystemWebExtensions ();
86                 protected internal abstract string GetUrl (ScriptManager scriptManager, bool zip);
87
88                 // This method is an example of particularily bad coding - .NET performs NO checks
89                 // on pathOrName!
90                 protected static string ReplaceExtension (string pathOrName)
91                 {
92                         // emulate .NET behavior
93                         if (pathOrName == null)
94                                 throw new NullReferenceException ();
95                         
96                         // We should check the length, but since .NET doesn't do that, we won't
97                         // either. Ugh.
98                         return pathOrName.Substring (0, pathOrName.Length - 2) + "debug.js";
99                 }
100
101                 internal static string GetScriptName (string releaseName, bool isDebugMode, string [] supportedUICultures, Assembly assembly, out WebResourceAttribute wra)
102                 {
103                         if (assembly != null)
104                                 VerifyAssemblyContainsResource (assembly, releaseName, out wra);
105                         else
106                                 wra = null;
107                         
108                         if (!isDebugMode && (supportedUICultures == null || supportedUICultures.Length == 0))
109                                 return releaseName;
110
111                         if (releaseName.Length < 3 || !releaseName.EndsWith (".js", StringComparison.OrdinalIgnoreCase))
112                                 throw new InvalidOperationException (String.Format ("'{0}' is not a valid script path.  The path must end in '.js'.", releaseName));
113                         
114                         StringBuilder sb = new StringBuilder (releaseName);
115                         sb.Length -= 3;
116                         if (isDebugMode)
117                                 sb.Append (".debug");
118                         string culture = Thread.CurrentThread.CurrentUICulture.Name;
119                         if (supportedUICultures != null && Array.IndexOf<string> (supportedUICultures, culture) >= 0)
120                                 sb.AppendFormat (".{0}", culture);
121                         sb.Append (".js");
122
123                         string ret = sb.ToString ();
124                         WebResourceAttribute debugWra;
125                         if (!CheckIfAssemblyContainsResource (assembly, ret, out debugWra))
126                                 return releaseName;
127                         wra = debugWra;
128                         
129                         return ret;
130                 }
131                 
132                 static void VerifyAssemblyContainsResource (Assembly assembly, string resourceName, out WebResourceAttribute wra)
133                 {
134                         var rce = new ResourceCacheEntry {
135                                 Assembly = assembly,
136                                 ResourceName = resourceName
137                         };
138
139                         WebResourceAttribute attr = null;
140                         if (!resourceCache.InsertOrGet ((uint)rce.GetHashCode (), rce, false, () => CheckIfAssemblyContainsResource (assembly, resourceName, out attr)))
141                                 throw new InvalidOperationException (String.Format ("Assembly '{0}' does not contain a Web resource with name '{1}'.",
142                                                                                     assembly.FullName, resourceName));
143                         wra = attr;
144                 }
145
146                 static bool CheckIfAssemblyContainsResource (Assembly assembly, string resourceName, out WebResourceAttribute wra)
147                 {
148                         foreach (WebResourceAttribute attr in assembly.GetCustomAttributes (typeof (WebResourceAttribute), false)) {
149                                 if (String.Compare (resourceName, attr.WebResource, StringComparison.Ordinal) == 0) {
150                                         using (Stream rs = assembly.GetManifestResourceStream (resourceName)) {
151                                                 if (rs == null)
152                                                         throw new InvalidOperationException (
153                                                                 String.Format ("Assembly '{0}' contains a Web resource with name '{1}' but does not contain an embedded resource with name '{1}'.",
154                                                                                assembly.FullName, resourceName)
155                                                         );
156                                         }
157                                         wra = attr;
158                                         return true;
159                                 
160                                 }
161                         }
162                         wra = null;
163                         return false;
164                 }
165
166                 sealed class ResourceCacheEntry
167                 {
168                         public Assembly Assembly;
169                         public string ResourceName;
170
171                         public override int GetHashCode ()
172                         {
173                                 int ret = 0;
174                                 if (Assembly != null)
175                                         ret ^= Assembly.GetHashCode ();
176                                 if (ResourceName != null)
177                                         ret ^= ResourceName.GetHashCode ();
178                                 return ret;
179                         }
180                 }
181         }
182 }