AppResourcesCompiler.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. //
  2. // System.Web.Compilation.AppResourceFilesCollection
  3. //
  4. // Authors:
  5. // Marek Habersack ([email protected])
  6. //
  7. // (C) 2006 Marek Habersack
  8. // (C) 2007-2009 Novell, Inc http://novell.com/
  9. //
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.CodeDom;
  32. using System.CodeDom.Compiler;
  33. using System.Collections;
  34. using System.Collections.Generic;
  35. using System.ComponentModel.Design;
  36. using System.Globalization;
  37. using System.IO;
  38. using System.Reflection;
  39. using System.Resources;
  40. using System.Text;
  41. using System.Web;
  42. using System.Web.Caching;
  43. using System.Web.Configuration;
  44. using System.Web.Util;
  45. namespace System.Web.Compilation
  46. {
  47. class AppResourcesCompiler
  48. {
  49. // This class fixes bug #466059
  50. class TypeResolutionService : ITypeResolutionService
  51. {
  52. List <Assembly> referencedAssemblies;
  53. Dictionary <string, Type> mappedTypes;
  54. public Assembly GetAssembly (AssemblyName name)
  55. {
  56. return GetAssembly (name, false);
  57. }
  58. public Assembly GetAssembly (AssemblyName name, bool throwOnError)
  59. {
  60. try {
  61. return Assembly.Load (name);
  62. } catch {
  63. if (throwOnError)
  64. throw;
  65. }
  66. return null;
  67. }
  68. public void ReferenceAssembly (AssemblyName name)
  69. {
  70. if (referencedAssemblies == null)
  71. referencedAssemblies = new List <Assembly> ();
  72. Assembly asm = GetAssembly (name, false);
  73. if (asm == null)
  74. return;
  75. if (referencedAssemblies.Contains (asm))
  76. return;
  77. referencedAssemblies.Add (asm);
  78. }
  79. public string GetPathOfAssembly (AssemblyName name)
  80. {
  81. if (name == null)
  82. return null;
  83. Assembly asm = GetAssembly (name, false);
  84. if (asm == null)
  85. return null;
  86. return asm.Location;
  87. }
  88. public Type GetType (string name)
  89. {
  90. return GetType (name, false, false);
  91. }
  92. public Type GetType (string name, bool throwOnError)
  93. {
  94. return GetType (name, throwOnError, false);
  95. }
  96. public Type GetType (string name, bool throwOnError, bool ignoreCase)
  97. {
  98. if (String.IsNullOrEmpty (name)) {
  99. if (throwOnError)
  100. throw new ArgumentNullException ("name");
  101. else
  102. return null;
  103. }
  104. int idx = name.IndexOf (',');
  105. Type type = null;
  106. if (idx == -1) {
  107. type = MapType (name, false);
  108. if (type != null)
  109. return type;
  110. type = FindInAssemblies (name, ignoreCase);
  111. if (type == null) {
  112. if (throwOnError)
  113. throw new InvalidOperationException ("Type '" + name + "' is not fully qualified and there are no referenced assemblies.");
  114. else
  115. return null;
  116. }
  117. return type;
  118. }
  119. type = MapType (name, true);
  120. if (type != null)
  121. return type;
  122. return Type.GetType (name, throwOnError, ignoreCase);
  123. }
  124. Type MapType (string name, bool full)
  125. {
  126. if (mappedTypes == null)
  127. mappedTypes = new Dictionary <string, Type> (StringComparer.Ordinal);
  128. Type ret;
  129. if (mappedTypes.TryGetValue (name, out ret))
  130. return ret;
  131. if (!full) {
  132. if (String.Compare (name, "ResXDataNode", StringComparison.Ordinal) == 0)
  133. return AddMappedType (name, typeof (ResXDataNode));
  134. if (String.Compare (name, "ResXFileRef", StringComparison.Ordinal) == 0)
  135. return AddMappedType (name, typeof (ResXFileRef));
  136. if (String.Compare (name, "ResXNullRef", StringComparison.Ordinal) == 0)
  137. return AddMappedType (name, typeof (ResXNullRef));
  138. if (String.Compare (name, "ResXResourceReader", StringComparison.Ordinal) == 0)
  139. return AddMappedType (name, typeof (ResXResourceReader));
  140. if (String.Compare (name, "ResXResourceWriter", StringComparison.Ordinal) == 0)
  141. return AddMappedType (name, typeof (ResXResourceWriter));
  142. return null;
  143. }
  144. if (name.IndexOf ("System.Windows.Forms") == -1)
  145. return null;
  146. if (name.IndexOf ("ResXDataNode", StringComparison.Ordinal) != -1)
  147. return AddMappedType (name, typeof (ResXDataNode));
  148. if (name.IndexOf ("ResXFileRef", StringComparison.Ordinal) != -1)
  149. return AddMappedType (name, typeof (ResXFileRef));
  150. if (name.IndexOf ("ResXNullRef", StringComparison.Ordinal) != -1)
  151. return AddMappedType (name, typeof (ResXNullRef));
  152. if (name.IndexOf ("ResXResourceReader", StringComparison.Ordinal) != -1)
  153. return AddMappedType (name, typeof (ResXResourceReader));
  154. if (name.IndexOf ("ResXResourceWriter", StringComparison.Ordinal) != -1)
  155. return AddMappedType (name, typeof (ResXResourceWriter));
  156. return null;
  157. }
  158. Type AddMappedType (string name, Type type)
  159. {
  160. mappedTypes.Add (name, type);
  161. return type;
  162. }
  163. Type FindInAssemblies (string name, bool ignoreCase)
  164. {
  165. Type ret = Type.GetType (name, false);
  166. if (ret != null)
  167. return ret;
  168. if (referencedAssemblies == null || referencedAssemblies.Count == 0)
  169. return null;
  170. foreach (Assembly asm in referencedAssemblies) {
  171. ret = asm.GetType (name, false, ignoreCase);
  172. if (ret != null)
  173. return ret;
  174. }
  175. return null;
  176. }
  177. }
  178. const string cachePrefix = "@@LocalResourcesAssemblies";
  179. bool isGlobal;
  180. AppResourceFilesCollection files;
  181. string tempDirectory;
  182. string virtualPath;
  183. Dictionary <string, List <string>> cultureFiles;
  184. List <string> defaultCultureFiles;
  185. string TempDirectory {
  186. get {
  187. if (tempDirectory != null)
  188. return tempDirectory;
  189. return (tempDirectory = AppDomain.CurrentDomain.SetupInformation.DynamicBase);
  190. }
  191. }
  192. public Dictionary <string, List <string>> CultureFiles {
  193. get { return cultureFiles; }
  194. }
  195. public List <string> DefaultCultureFiles {
  196. get { return defaultCultureFiles; }
  197. }
  198. static AppResourcesCompiler ()
  199. {
  200. if (!BuildManager.IsPrecompiled)
  201. return;
  202. string[] binDirAssemblies = HttpApplication.BinDirectoryAssemblies;
  203. if (binDirAssemblies == null || binDirAssemblies.Length == 0)
  204. return;
  205. string name;
  206. Assembly asm;
  207. foreach (string asmPath in binDirAssemblies) {
  208. if (String.IsNullOrEmpty (asmPath))
  209. continue;
  210. name = Path.GetFileName (asmPath);
  211. if (name.StartsWith ("App_LocalResources.", StringComparison.OrdinalIgnoreCase)) {
  212. string virtualPath = GetPrecompiledVirtualPath (asmPath);
  213. if (String.IsNullOrEmpty (virtualPath))
  214. continue;
  215. asm = LoadAssembly (asmPath);
  216. if (asm == null)
  217. continue;
  218. AddAssemblyToCache (virtualPath, asm);
  219. continue;
  220. }
  221. if (String.Compare (name, "App_GlobalResources.dll", StringComparison.OrdinalIgnoreCase) != 0)
  222. continue;
  223. asm = LoadAssembly (asmPath);
  224. if (asm == null)
  225. continue;
  226. HttpContext.AppGlobalResourcesAssembly = asm;
  227. }
  228. }
  229. public AppResourcesCompiler (HttpContext context)
  230. {
  231. this.isGlobal = true;
  232. this.files = new AppResourceFilesCollection (context);
  233. this.cultureFiles = new Dictionary <string, List <string>> (StringComparer.OrdinalIgnoreCase);
  234. }
  235. public AppResourcesCompiler (string virtualPath)
  236. {
  237. this.virtualPath = virtualPath;
  238. this.isGlobal = false;
  239. this.files = new AppResourceFilesCollection (HttpContext.Current.Request.MapPath (virtualPath));
  240. this.cultureFiles = new Dictionary <string, List <string>> (StringComparer.OrdinalIgnoreCase);
  241. }
  242. static Assembly LoadAssembly (string asmPath)
  243. {
  244. try {
  245. return Assembly.LoadFrom (asmPath);
  246. } catch (BadImageFormatException) {
  247. // ignore
  248. return null;
  249. }
  250. }
  251. static string GetPrecompiledVirtualPath (string asmPath)
  252. {
  253. string compiledFile = Path.ChangeExtension (asmPath, ".compiled");
  254. if (!File.Exists (compiledFile))
  255. return null;
  256. var pfile = new PreservationFile (compiledFile);
  257. string virtualPath = pfile.VirtualPath;
  258. if (String.IsNullOrEmpty (virtualPath))
  259. return "/";
  260. if (virtualPath.EndsWith ("/App_LocalResources/", StringComparison.OrdinalIgnoreCase))
  261. virtualPath = virtualPath.Substring (0, virtualPath.Length - 19);
  262. return virtualPath;
  263. }
  264. public Assembly Compile ()
  265. {
  266. files.Collect ();
  267. if (!files.HasFiles)
  268. return null;
  269. if (isGlobal)
  270. return CompileGlobal ();
  271. else
  272. return CompileLocal ();
  273. }
  274. Assembly CompileGlobal ()
  275. {
  276. string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
  277. "App_GlobalResources",
  278. "dll",
  279. OnCreateRandomFile) as string;
  280. if (assemblyPath == null)
  281. throw new ApplicationException ("Failed to create global resources assembly");
  282. List <string>[] fileGroups = GroupGlobalFiles ();
  283. if (fileGroups == null || fileGroups.Length == 0)
  284. return null;
  285. CodeCompileUnit unit = new CodeCompileUnit ();
  286. CodeNamespace ns = new CodeNamespace (null);
  287. ns.Imports.Add (new CodeNamespaceImport ("System"));
  288. ns.Imports.Add (new CodeNamespaceImport ("System.Globalization"));
  289. ns.Imports.Add (new CodeNamespaceImport ("System.Reflection"));
  290. ns.Imports.Add (new CodeNamespaceImport ("System.Resources"));
  291. unit.Namespaces.Add (ns);
  292. AppResourcesAssemblyBuilder builder = new AppResourcesAssemblyBuilder ("App_GlobalResources", assemblyPath,
  293. this);
  294. CodeDomProvider provider = builder.Provider;
  295. Dictionary <string,bool> assemblies = new Dictionary<string,bool> ();
  296. foreach (List<string> ls in fileGroups)
  297. DomFromResource (ls [0], unit, assemblies, provider);
  298. foreach (KeyValuePair<string,bool> de in assemblies)
  299. unit.ReferencedAssemblies.Add (de.Key);
  300. builder.Build (unit);
  301. HttpContext.AppGlobalResourcesAssembly = builder.MainAssembly;
  302. return builder.MainAssembly;
  303. }
  304. Assembly CompileLocal ()
  305. {
  306. if (String.IsNullOrEmpty (virtualPath))
  307. return null;
  308. Assembly cached = GetCachedLocalResourcesAssembly (virtualPath);
  309. if (cached != null)
  310. return cached;
  311. string prefix;
  312. if (virtualPath == "/")
  313. prefix = "App_LocalResources.root";
  314. else
  315. prefix = "App_LocalResources" + virtualPath.Replace ('/', '.');
  316. string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
  317. prefix,
  318. "dll",
  319. OnCreateRandomFile) as string;
  320. if (assemblyPath == null)
  321. throw new ApplicationException ("Failed to create local resources assembly");
  322. List<AppResourceFileInfo> files = this.files.Files;
  323. foreach (AppResourceFileInfo arfi in files)
  324. GetResourceFile (arfi, true);
  325. AppResourcesAssemblyBuilder builder = new AppResourcesAssemblyBuilder ("App_LocalResources", assemblyPath,
  326. this);
  327. builder.Build ();
  328. Assembly ret = builder.MainAssembly;
  329. if (ret != null)
  330. AddAssemblyToCache (virtualPath, ret);
  331. return ret;
  332. }
  333. internal static Assembly GetCachedLocalResourcesAssembly (string path)
  334. {
  335. Dictionary <string, Assembly> cache;
  336. cache = HttpRuntime.InternalCache[cachePrefix] as Dictionary <string, Assembly>;
  337. if (cache == null || !cache.ContainsKey (path))
  338. return null;
  339. return cache [path];
  340. }
  341. static void AddAssemblyToCache (string path, Assembly asm)
  342. {
  343. Cache runtimeCache = HttpRuntime.InternalCache;
  344. Dictionary <string, Assembly> cache;
  345. cache = runtimeCache[cachePrefix] as Dictionary <string, Assembly>;
  346. if (cache == null)
  347. cache = new Dictionary <string, Assembly> ();
  348. cache [path] = asm;
  349. runtimeCache.Insert (cachePrefix, cache);
  350. }
  351. uint CountChars (char c, string s)
  352. {
  353. uint ret = 0;
  354. foreach (char ch in s) {
  355. if (ch == c)
  356. ret++;
  357. }
  358. return ret;
  359. }
  360. string IsFileCultureValid (string fileName)
  361. {
  362. string tmp = Path.GetFileNameWithoutExtension (fileName);
  363. tmp = Path.GetExtension (tmp);
  364. if (tmp != null && tmp.Length > 0) {
  365. tmp = tmp.Substring (1);
  366. try {
  367. CultureInfo.GetCultureInfo (tmp);
  368. return tmp;
  369. } catch {
  370. return null;
  371. }
  372. }
  373. return null;
  374. }
  375. string GetResourceFile (AppResourceFileInfo arfi, bool local)
  376. {
  377. string resfile;
  378. if (arfi.Kind == AppResourceFileKind.ResX)
  379. resfile = CompileResource (arfi, local);
  380. else
  381. resfile = arfi.Info.FullName;
  382. if (!String.IsNullOrEmpty (resfile)) {
  383. string culture = IsFileCultureValid (resfile);
  384. List <string> cfiles;
  385. if (culture != null) {
  386. if (cultureFiles.ContainsKey (culture))
  387. cfiles = cultureFiles [culture];
  388. else {
  389. cfiles = new List <string> (1);
  390. cultureFiles [culture] = cfiles;
  391. }
  392. } else {
  393. if (defaultCultureFiles == null)
  394. defaultCultureFiles = new List <string> ();
  395. cfiles = defaultCultureFiles;
  396. }
  397. cfiles.Add (resfile);
  398. }
  399. return resfile;
  400. }
  401. List <string>[] GroupGlobalFiles ()
  402. {
  403. List<AppResourceFileInfo> files = this.files.Files;
  404. List<List<string>> groups = new List<List<string>> ();
  405. AppResourcesLengthComparer<List<string>> lcList = new AppResourcesLengthComparer<List<string>> ();
  406. string tmp, s, basename;
  407. uint basedots, filedots;
  408. AppResourceFileInfo defaultFile;
  409. foreach (AppResourceFileInfo arfi in files) {
  410. if (arfi.Kind != AppResourceFileKind.ResX && arfi.Kind != AppResourceFileKind.Resource)
  411. continue;
  412. s = arfi.Info.FullName;
  413. basename = Path.GetFileNameWithoutExtension (s);
  414. basedots = CountChars ('.', basename);
  415. defaultFile = null;
  416. // If there are any files that start with this baseName, we have a default file
  417. foreach (AppResourceFileInfo fi in files) {
  418. if (fi.Seen)
  419. continue;
  420. string s2 = fi.Info.FullName;
  421. if (s2 == null || s == s2)
  422. continue;
  423. tmp = Path.GetFileNameWithoutExtension (s2);
  424. filedots = CountChars ('.', tmp);
  425. if (filedots == basedots + 1 && tmp.StartsWith (basename)) {
  426. if (IsFileCultureValid (s2) != null) {
  427. // A valid translated file for this name
  428. defaultFile = arfi;
  429. break;
  430. } else {
  431. // This file shares the base name, but the culture is invalid - we must
  432. // ignore it since the name of the generated strongly typed class for this
  433. // resource will clash with the one generated from the default file with
  434. // the given basename.
  435. fi.Seen = true;
  436. }
  437. }
  438. }
  439. if (defaultFile != null) {
  440. List<string> al = new List<string> ();
  441. al.Add (GetResourceFile (arfi, false));
  442. arfi.Seen = true;
  443. groups.Add (al);
  444. }
  445. }
  446. groups.Sort (lcList);
  447. string tmp2;
  448. // Now find their translated counterparts
  449. foreach (List<string> al in groups) {
  450. s = al [0];
  451. tmp = Path.GetFileNameWithoutExtension (s);
  452. if (tmp.StartsWith ("Resources."))
  453. tmp = tmp.Substring (10);
  454. foreach (AppResourceFileInfo arfi in files) {
  455. if (arfi.Seen)
  456. continue;
  457. s = arfi.Info.FullName;
  458. if (s == null)
  459. continue;
  460. tmp2 = arfi.Info.Name;
  461. if (tmp2.StartsWith (tmp)) {
  462. al.Add (GetResourceFile (arfi, false));
  463. arfi.Seen = true;
  464. }
  465. }
  466. }
  467. // Anything that's left here might be orphans or lone default files.
  468. // For those files we check the part following the last dot
  469. // before the .resx/.resource extensions and test whether it's a registered
  470. // culture or not. If it is not a culture, then we have a
  471. // default file that doesn't have any translations. Otherwise,
  472. // the file is ignored (it's the same thing MS.NET does)
  473. foreach (AppResourceFileInfo arfi in files) {
  474. if (arfi.Seen)
  475. continue;
  476. if (IsFileCultureValid (arfi.Info.FullName) != null)
  477. continue; // Culture found, we reject the file
  478. // A single default file, create a group
  479. List<string> al = new List<string> ();
  480. al.Add (GetResourceFile (arfi, false));
  481. groups.Add (al);
  482. }
  483. groups.Sort (lcList);
  484. return groups.ToArray ();
  485. }
  486. // CodeDOM generation
  487. void DomFromResource (string resfile, CodeCompileUnit unit, Dictionary <string,bool> assemblies,
  488. CodeDomProvider provider)
  489. {
  490. if (String.IsNullOrEmpty (resfile))
  491. return;
  492. string fname, nsname, classname;
  493. fname = Path.GetFileNameWithoutExtension (resfile);
  494. nsname = Path.GetFileNameWithoutExtension (fname);
  495. classname = Path.GetExtension (fname);
  496. if (classname == null || classname.Length == 0) {
  497. classname = nsname;
  498. nsname = "Resources";
  499. } else {
  500. if (!nsname.StartsWith ("Resources", StringComparison.InvariantCulture))
  501. nsname = String.Concat ("Resources.", nsname);
  502. classname = classname.Substring(1);
  503. }
  504. if (!String.IsNullOrEmpty (classname))
  505. classname = classname.Replace ('.', '_');
  506. if (!String.IsNullOrEmpty (nsname))
  507. nsname = nsname.Replace ('.', '_');
  508. if (!provider.IsValidIdentifier (nsname) || !provider.IsValidIdentifier (classname))
  509. throw new ApplicationException ("Invalid resource file name.");
  510. ResourceReader res;
  511. try {
  512. res = new ResourceReader (resfile);
  513. } catch (ArgumentException) {
  514. // invalid stream, probably empty - ignore silently and abort
  515. return;
  516. }
  517. CodeNamespace ns = new CodeNamespace (nsname);
  518. CodeTypeDeclaration cls = new CodeTypeDeclaration (classname);
  519. cls.IsClass = true;
  520. cls.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;
  521. CodeMemberField cmf = new CodeMemberField (typeof(CultureInfo), "_culture");
  522. cmf.InitExpression = new CodePrimitiveExpression (null);
  523. cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
  524. cls.Members.Add (cmf);
  525. cmf = new CodeMemberField (typeof(ResourceManager), "_resourceManager");
  526. cmf.InitExpression = new CodePrimitiveExpression (null);
  527. cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
  528. cls.Members.Add (cmf);
  529. // Property: ResourceManager
  530. CodeMemberProperty cmp = new CodeMemberProperty ();
  531. cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
  532. cmp.Name = "ResourceManager";
  533. cmp.HasGet = true;
  534. cmp.Type = new CodeTypeReference (typeof(ResourceManager));
  535. CodePropertyResourceManagerGet (cmp.GetStatements, resfile, classname);
  536. cls.Members.Add (cmp);
  537. // Property: Culture
  538. cmp = new CodeMemberProperty ();
  539. cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final;
  540. cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
  541. cmp.Name = "Culture";
  542. cmp.HasGet = true;
  543. cmp.HasSet = true;
  544. cmp.Type = new CodeTypeReference (typeof(CultureInfo));
  545. CodePropertyGenericGet (cmp.GetStatements, "_culture", classname);
  546. CodePropertyGenericSet (cmp.SetStatements, "_culture", classname);
  547. cls.Members.Add (cmp);
  548. // Add the resource properties
  549. Dictionary<string,bool> imports = new Dictionary<string,bool> ();
  550. try {
  551. foreach (DictionaryEntry de in res) {
  552. Type type = de.Value.GetType ();
  553. if (!imports.ContainsKey (type.Namespace))
  554. imports [type.Namespace] = true;
  555. string asname = new AssemblyName (type.Assembly.FullName).Name;
  556. if (!assemblies.ContainsKey (asname))
  557. assemblies [asname] = true;
  558. cmp = new CodeMemberProperty ();
  559. cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
  560. cmp.Name = SanitizeResourceName (provider, (string)de.Key);
  561. cmp.HasGet = true;
  562. CodePropertyResourceGet (cmp.GetStatements, (string)de.Key, type, classname);
  563. cmp.Type = new CodeTypeReference (type);
  564. cls.Members.Add (cmp);
  565. }
  566. } catch (Exception ex) {
  567. throw new ApplicationException ("Failed to compile global resources.", ex);
  568. }
  569. foreach (KeyValuePair<string,bool> de in imports)
  570. ns.Imports.Add (new CodeNamespaceImport(de.Key));
  571. ns.Types.Add (cls);
  572. unit.Namespaces.Add (ns);
  573. }
  574. static bool is_identifier_start_character (int c)
  575. {
  576. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter ((char)c);
  577. }
  578. static bool is_identifier_part_character (char c)
  579. {
  580. if (c >= 'a' && c <= 'z')
  581. return true;
  582. if (c >= 'A' && c <= 'Z')
  583. return true;
  584. if (c == '_' || (c >= '0' && c <= '9'))
  585. return true;
  586. if (c < 0x80)
  587. return false;
  588. return Char.IsLetter (c) || Char.GetUnicodeCategory (c) == UnicodeCategory.ConnectorPunctuation;
  589. }
  590. string SanitizeResourceName (CodeDomProvider provider, string name)
  591. {
  592. if (provider.IsValidIdentifier (name))
  593. return provider.CreateEscapedIdentifier (name);
  594. var sb = new StringBuilder ();
  595. char ch = name [0];
  596. if (is_identifier_start_character (ch))
  597. sb.Append (ch);
  598. else {
  599. sb.Append ('_');
  600. if (ch >= '0' && ch <= '9')
  601. sb.Append (ch);
  602. }
  603. for (int i = 1; i < name.Length; i++) {
  604. ch = name [i];
  605. if (is_identifier_part_character (ch))
  606. sb.Append (ch);
  607. else
  608. sb.Append ('_');
  609. }
  610. return provider.CreateEscapedIdentifier (sb.ToString ());
  611. }
  612. CodeObjectCreateExpression NewResourceManager (string name, string typename)
  613. {
  614. CodeExpression resname = new CodePrimitiveExpression (name);
  615. CodePropertyReferenceExpression asm = new CodePropertyReferenceExpression (
  616. new CodeTypeOfExpression (new CodeTypeReference (typename)),
  617. "Assembly");
  618. return new CodeObjectCreateExpression ("System.Resources.ResourceManager",
  619. new CodeExpression [] {resname, asm});
  620. }
  621. void CodePropertyResourceManagerGet (CodeStatementCollection csc, string resfile, string typename)
  622. {
  623. string name = Path.GetFileNameWithoutExtension (resfile);
  624. CodeStatement st;
  625. CodeExpression exp;
  626. exp = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), "_resourceManager");
  627. st = new CodeConditionStatement (
  628. new CodeBinaryOperatorExpression (
  629. exp,
  630. CodeBinaryOperatorType.IdentityInequality,
  631. new CodePrimitiveExpression (null)),
  632. new CodeStatement [] { new CodeMethodReturnStatement (exp) });
  633. csc.Add (st);
  634. st = new CodeAssignStatement (exp, NewResourceManager (name, typename));
  635. csc.Add (st);
  636. csc.Add (new CodeMethodReturnStatement (exp));
  637. }
  638. void CodePropertyResourceGet (CodeStatementCollection csc, string resname, Type restype, string typename)
  639. {
  640. CodeStatement st = new CodeVariableDeclarationStatement (
  641. typeof (ResourceManager),
  642. "rm",
  643. new CodePropertyReferenceExpression (
  644. new CodeTypeReferenceExpression (typename), "ResourceManager"));
  645. csc.Add (st);
  646. st = new CodeConditionStatement (
  647. new CodeBinaryOperatorExpression (
  648. new CodeVariableReferenceExpression ("rm"),
  649. CodeBinaryOperatorType.IdentityEquality,
  650. new CodePrimitiveExpression (null)),
  651. new CodeStatement [] { new CodeMethodReturnStatement (new CodePrimitiveExpression (null)) });
  652. csc.Add (st);
  653. bool gotstr = (restype == typeof (string));
  654. CodeExpression exp = new CodeMethodInvokeExpression (
  655. new CodeVariableReferenceExpression ("rm"),
  656. gotstr ? "GetString" : "GetObject",
  657. new CodeExpression [] { new CodePrimitiveExpression (resname),
  658. new CodeFieldReferenceExpression (
  659. new CodeTypeReferenceExpression (typename), "_culture") });
  660. st = new CodeVariableDeclarationStatement (
  661. restype,
  662. "obj",
  663. gotstr ? exp : new CodeCastExpression (restype, exp));
  664. csc.Add (st);
  665. csc.Add (new CodeMethodReturnStatement (new CodeVariableReferenceExpression ("obj")));
  666. }
  667. void CodePropertyGenericGet (CodeStatementCollection csc, string field, string typename)
  668. {
  669. csc.Add(new CodeMethodReturnStatement (
  670. new CodeFieldReferenceExpression (
  671. new CodeTypeReferenceExpression (typename), field)));
  672. }
  673. void CodePropertyGenericSet (CodeStatementCollection csc, string field, string typename)
  674. {
  675. csc.Add(new CodeAssignStatement (
  676. new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), field),
  677. new CodeVariableReferenceExpression ("value")));
  678. }
  679. string CompileResource (AppResourceFileInfo arfi, bool local)
  680. {
  681. string path = arfi.Info.FullName;
  682. string rname = Path.GetFileNameWithoutExtension (path) + ".resources";
  683. if (!local)
  684. rname = "Resources." + rname;
  685. string resource = Path.Combine (TempDirectory, rname);
  686. FileStream source = null, destination = null;
  687. IResourceReader reader = null;
  688. ResourceWriter writer = null;
  689. try {
  690. source = new FileStream (path, FileMode.Open, FileAccess.Read);
  691. destination = new FileStream (resource, FileMode.Create, FileAccess.Write);
  692. reader = GetReaderForKind (arfi.Kind, source, path);
  693. writer = new ResourceWriter (destination);
  694. foreach (DictionaryEntry de in reader) {
  695. object val = de.Value;
  696. if (val is string)
  697. writer.AddResource ((string)de.Key, (string)val);
  698. else
  699. writer.AddResource ((string)de.Key, val);
  700. }
  701. } catch (Exception ex) {
  702. throw new HttpException ("Failed to compile resource file", ex);
  703. } finally {
  704. if (reader != null)
  705. reader.Dispose ();
  706. if (source != null)
  707. source.Dispose ();
  708. if (writer != null)
  709. writer.Dispose ();
  710. if (destination != null)
  711. destination.Dispose ();
  712. }
  713. return resource;
  714. }
  715. IResourceReader GetReaderForKind (AppResourceFileKind kind, Stream stream, string path)
  716. {
  717. switch (kind) {
  718. case AppResourceFileKind.ResX:
  719. var ret = new ResXResourceReader (stream, new TypeResolutionService ());
  720. if (!String.IsNullOrEmpty (path))
  721. ret.BasePath = Path.GetDirectoryName (path);
  722. return ret;
  723. case AppResourceFileKind.Resource:
  724. return new ResourceReader (stream);
  725. default:
  726. return null;
  727. }
  728. }
  729. object OnCreateRandomFile (string path)
  730. {
  731. FileStream f = new FileStream (path, FileMode.CreateNew);
  732. f.Close ();
  733. return path;
  734. }
  735. };
  736. };