* PageThemeCompiler.cs: let property builders through, stop the
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AppResourcesCompiler.cs
1 //
2 // System.Web.Compilation.AppResourceFilesCollection
3 //
4 // Authors:
5 //   Marek Habersack (grendello@gmail.com)
6 //
7 // (C) 2006 Marek Habersack
8 //
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 #if NET_2_0
31 using System;
32 using System.CodeDom;
33 using System.CodeDom.Compiler;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Globalization;
37 using System.IO;
38 using System.Reflection;
39 using System.Resources;
40 using System.Web;
41 using System.Web.Caching;
42 using System.Web.Configuration;
43 using System.Web.Util;
44
45 namespace System.Web.Compilation 
46 {
47         internal class AppResourcesCompiler
48         {
49                 const string cachePrefix = "@@LocalResourcesAssemblies";
50                 
51                 bool isGlobal;
52                 HttpContext context;
53                 AppResourceFilesCollection files;
54                 string tempDirectory;
55                 string virtualPath;
56                 
57                 string TempDirectory {
58                         get {
59                                 if (tempDirectory != null)
60                                         return tempDirectory;
61                                 return (tempDirectory = AppDomain.CurrentDomain.SetupInformation.DynamicBase);
62                         }
63                 }
64                 
65                 public AppResourcesCompiler (HttpContext context)
66                 {
67                         this.context = context;
68                         this.isGlobal = true;
69                         this.files = new AppResourceFilesCollection (context);
70                 }
71
72                 public AppResourcesCompiler (string virtualPath)
73                 {
74
75                         this.virtualPath = virtualPath;
76                         this.isGlobal = false;
77                         this.files = new AppResourceFilesCollection (HttpContext.Current.Request.MapPath (virtualPath));
78                 }
79                 
80                 public Assembly Compile ()
81                 {
82                         files.Collect ();
83                         if (!files.HasFiles)
84                                 return null;
85                         if (isGlobal)
86                                 return CompileGlobal ();
87                         else
88                                 return CompileLocal ();
89                 }
90
91                 Assembly CompileGlobal ()
92                 {
93                         string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
94                                                                              "App_GlobalResources",
95                                                                              "dll",
96                                                                              OnCreateRandomFile) as string;
97
98                         if (assemblyPath == null)
99                                 throw new ApplicationException ("Failed to create global resources assembly");
100                         
101                         CompilationSection config = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection;
102                         if (config == null || !CodeDomProvider.IsDefinedLanguage (config.DefaultLanguage))
103                                 throw new ApplicationException ("Could not get the default compiler.");
104                         CompilerInfo ci = CodeDomProvider.GetCompilerInfo (config.DefaultLanguage);
105                         if (ci == null || !ci.IsCodeDomProviderTypeValid)
106                                 throw new ApplicationException ("Failed to obtain the default compiler information.");
107
108                         CompilerParameters cp = ci.CreateDefaultCompilerParameters ();
109                         cp.OutputAssembly = assemblyPath;
110                         cp.GenerateExecutable = false;
111                         cp.TreatWarningsAsErrors = true;
112                         cp.IncludeDebugInformation = config.Debug;
113                         
114                         List <string>[] fileGroups = GroupGlobalFiles (cp);
115                         if (fileGroups == null || fileGroups.Length == 0)
116                                 return null;
117
118                         CodeCompileUnit unit = new CodeCompileUnit ();
119                         CodeNamespace ns = new CodeNamespace (null);
120                         ns.Imports.Add (new CodeNamespaceImport ("System"));
121                         ns.Imports.Add (new CodeNamespaceImport ("System.Globalization"));
122                         ns.Imports.Add (new CodeNamespaceImport ("System.Reflection"));
123                         ns.Imports.Add (new CodeNamespaceImport ("System.Resources"));
124                         unit.Namespaces.Add (ns);
125
126                         CodeDomProvider provider;
127                         provider = ci.CreateProvider ();
128                         if (provider == null)
129                                 throw new ApplicationException ("Failed to instantiate the default compiler.");
130                         
131                         Dictionary <string,bool> assemblies = new Dictionary<string,bool> ();
132                         foreach (List<string> ls in fileGroups)
133                                 DomFromResource (ls [0], unit, assemblies, provider);
134                         foreach (KeyValuePair<string,bool> de in assemblies)
135                                 unit.ReferencedAssemblies.Add (de.Key);
136                         
137                         AssemblyBuilder abuilder = new AssemblyBuilder (provider);
138                         abuilder.AddCodeCompileUnit (unit);
139
140                         CompilerResults results = abuilder.BuildAssembly (cp);
141                         Assembly ret = null;
142                         
143                         if (results.Errors.Count == 0) {
144                                 ret = results.CompiledAssembly;
145                                 BuildManager.TopLevelAssemblies.Add (ret);
146                                 HttpContext.AppGlobalResourcesAssembly = ret;
147                         } else {
148                                 if (context.IsCustomErrorEnabled)
149                                         throw new ApplicationException ("An error occurred while compiling global resources.");
150                                 throw new CompilationException (null, results.Errors, null);
151                         }
152                         HttpRuntime.WritePreservationFile (ret, "App_GlobalResources");
153                         HttpRuntime.EnableAssemblyMapping (true);
154
155                         return ret;
156                 }
157
158                 Assembly CompileLocal ()
159                 {
160                         if (String.IsNullOrEmpty (virtualPath))
161                                 return null;
162                         
163                         Assembly cached = GetCachedLocalResourcesAssembly (virtualPath);
164                         if (cached != null)
165                                 return cached;
166                         
167                         string prefix;
168                         if (virtualPath == "/")
169                                 prefix = "App_LocalResources.root";
170                         else
171                                 prefix = "App_LocalResources" + virtualPath.Replace ('/', '.');
172                         
173                         string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
174                                                                              prefix,
175                                                                              "dll",
176                                                                              OnCreateRandomFile) as string;
177                         if (assemblyPath == null)
178                                 throw new ApplicationException ("Failed to create global resources assembly");
179                         
180                         CompilationSection config = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection;
181                         if (config == null || !CodeDomProvider.IsDefinedLanguage (config.DefaultLanguage))
182                                 throw new ApplicationException ("Could not get the default compiler.");
183                         CompilerInfo ci = CodeDomProvider.GetCompilerInfo (config.DefaultLanguage);
184                         if (ci == null || !ci.IsCodeDomProviderTypeValid)
185                                 throw new ApplicationException ("Failed to obtain the default compiler information.");
186
187                         CompilerParameters cp = ci.CreateDefaultCompilerParameters ();
188                         cp.OutputAssembly = assemblyPath;
189                         cp.GenerateExecutable = false;
190                         cp.TreatWarningsAsErrors = true;
191                         cp.IncludeDebugInformation = config.Debug;
192
193                         List<AppResourceFileInfo> files = this.files.Files;
194                         foreach (AppResourceFileInfo arfi in files)
195                                 GetResourceFile (arfi, cp);
196
197                         CodeDomProvider provider;
198                         provider = ci.CreateProvider ();
199                         if (provider == null)
200                                 throw new ApplicationException ("Failed to instantiate the default compiler.");
201                         
202                         AssemblyBuilder abuilder = new AssemblyBuilder (provider);
203                         CompilerResults results = abuilder.BuildAssembly (cp);
204                         Assembly ret = null;
205                         
206                         if (results.Errors.Count == 0) {
207                                 ret = results.CompiledAssembly;
208                                 AddAssemblyToCache (virtualPath, ret);
209                         } else {
210                                 if (context.IsCustomErrorEnabled)
211                                         throw new ApplicationException ("An error occurred while compiling global resources.");
212                                 throw new CompilationException (null, results.Errors, null);
213                         }
214
215                         return ret;
216                 }
217                 
218                 internal static Assembly GetCachedLocalResourcesAssembly (string path)
219                 {
220                         Dictionary <string, Assembly> cache;
221
222                         cache = HttpRuntime.Cache[cachePrefix] as Dictionary <string, Assembly>;
223                         if (cache == null || !cache.ContainsKey (path))
224                                 return null;
225                         return cache [path];
226                 }
227                 
228                 void AddAssemblyToCache (string path, Assembly asm)
229                 {
230                         Cache runtimeCache = HttpRuntime.Cache;
231                         Dictionary <string, Assembly> cache;
232                         
233                         cache = runtimeCache[cachePrefix] as Dictionary <string, Assembly>;
234                         if (cache == null)
235                                 cache = new Dictionary <string, Assembly> ();
236                         cache [path] = asm;
237                         runtimeCache.Insert (cachePrefix, cache);
238                 }
239                 
240                 uint CountChars (char c, string s)
241                 {
242                         uint ret = 0;
243                         foreach (char ch in s) {
244                                 if (ch == c)
245                                         ret++;
246                         }
247                         return ret;
248                 }
249
250                 bool IsFileCultureValid (string fileName)
251                 {
252                     string tmp = Path.GetFileNameWithoutExtension (fileName);
253                     tmp = Path.GetExtension (tmp);
254                     if (tmp != null && tmp.Length > 0) {
255                               tmp = tmp.Substring (1);
256                             try {
257                                 CultureInfo.GetCultureInfo (tmp);
258                                 return true;
259                             } catch {
260                                 return false;
261                             }
262                     } 
263                     return false;
264                 }
265
266                 string GetResourceFile (AppResourceFileInfo arfi, CompilerParameters cp)
267                 {
268                         string resfile;
269                         if (arfi.Kind == AppResourceFileKind.ResX)
270                                 resfile = CompileResource (arfi);
271                         else
272                                 resfile = arfi.Info.FullName;
273                         if (!String.IsNullOrEmpty (resfile))
274                                 cp.EmbeddedResources.Add (resfile);
275                         return resfile;
276                 }
277                 
278                 List <string>[] GroupGlobalFiles (CompilerParameters cp)
279                 {
280                         List<AppResourceFileInfo> files = this.files.Files;
281                         List<List<string>> groups = new List<List<string>> ();
282                         AppResourcesLengthComparer<List<string>> lcList = new AppResourcesLengthComparer<List<string>> ();
283                         
284                         string tmp, s, basename;
285                         uint basedots, filedots;
286                         AppResourceFileInfo defaultFile;
287                         
288                         foreach (AppResourceFileInfo arfi in files) {
289                                 if (arfi.Kind != AppResourceFileKind.ResX && arfi.Kind != AppResourceFileKind.Resource)
290                                         continue;
291
292                                 s = arfi.Info.FullName;
293                                 basename = Path.GetFileNameWithoutExtension (s);
294                                 basedots = CountChars ('.', basename);
295                                 defaultFile = null;
296                                 
297                                 // If there are any files that start with this baseName, we have a default file
298                                 foreach (AppResourceFileInfo fi in files) {
299                                         if (fi.Seen)
300                                                 continue;
301                                         
302                                         string s2 = fi.Info.FullName;
303                                         if (s2 == null || s == s2)
304                                                 continue;
305                                         tmp = Path.GetFileNameWithoutExtension (s2);
306                                         filedots = CountChars ('.', tmp);
307
308                                         if (filedots == basedots + 1 && tmp.StartsWith (basename)) {
309                                                 if (IsFileCultureValid (s2)) {
310                                                         // A valid translated file for this name
311                                                         defaultFile = arfi;
312                                                         break;
313                                                 } else {
314                                                         // This file shares the base name, but the culture is invalid - we must
315                                                         // ignore it since the name of the generated strongly typed class for this
316                                                         // resource will clash with the one generated from the default file with
317                                                         // the given basename.
318                                                         fi.Seen = true;
319                                                 }
320                                         }
321                                 }
322                                 if (defaultFile != null) {
323                                         List<string> al = new List<string> ();
324                                         al.Add (GetResourceFile (arfi, cp));
325                                         arfi.Seen = true;
326                                         groups.Add (al);
327                                         
328                                 }
329                         }
330                         groups.Sort (lcList);
331
332                         string tmp2;
333                         // Now find their translated counterparts
334                         foreach (List<string> al in groups) {
335                                 s = al [0];
336                                 tmp = Path.GetFileNameWithoutExtension (s);
337                                 foreach (AppResourceFileInfo arfi in files) {
338                                         if (arfi.Seen)
339                                                 continue;
340                                         
341                                         s = arfi.Info.FullName;
342                                         if (s == null)
343                                                 continue;
344                                         tmp2 = arfi.Info.Name;
345                                         if (tmp2.StartsWith (tmp)) {
346                                                 al.Add (GetResourceFile (arfi, cp));
347                                                 arfi.Seen = true;
348                                         }
349                                 }
350                         }
351
352                         // Anything that's left here might be orphans or lone default files.
353                         // For those files we check the part following the last dot
354                         // before the .resx/.resource extensions and test whether it's a registered
355                         // culture or not. If it is not a culture, then we have a
356                         // default file that doesn't have any translations. Otherwise,
357                         // the file is ignored (it's the same thing MS.NET does)
358                         foreach (AppResourceFileInfo arfi in files) {
359                                 if (arfi.Seen)
360                                         continue;
361
362                                 if (IsFileCultureValid (arfi.Info.FullName))
363                                         continue; // Culture found, we reject the file
364
365                                 // A single default file, create a group
366                                 List<string> al = new List<string> ();
367                                 al.Add (GetResourceFile (arfi, cp));
368                                 groups.Add (al);
369                         }
370                         groups.Sort (lcList);
371                         return groups.ToArray ();
372                 }
373
374                 // CodeDOM generation
375                 void DomFromResource (string resfile, CodeCompileUnit unit, Dictionary <string,bool> assemblies,
376                                       CodeDomProvider provider)
377                 {
378                         if (String.IsNullOrEmpty (resfile))
379                                 return;
380
381                         string fname, nsname, classname;
382
383                         fname = Path.GetFileNameWithoutExtension (resfile);
384                         nsname = Path.GetFileNameWithoutExtension (fname);
385                         classname = Path.GetExtension (fname);
386                         if (classname == null || classname.Length == 0) {
387                                 classname = nsname;
388                                 nsname = "Resources";
389                         } else {
390                                 if (!nsname.StartsWith ("Resources", StringComparison.InvariantCulture))
391                                         nsname = String.Format ("Resources.{0}", nsname);
392                                 classname = classname.Substring(1);
393                         }
394                         
395                         if (!provider.IsValidIdentifier (nsname) || !provider.IsValidIdentifier (classname))
396                                 throw new ApplicationException ("Invalid resource file name.");
397
398                         ResourceReader res;
399                         try {
400                                 res = new ResourceReader (resfile);
401                         } catch (ArgumentException) {
402                                 // invalid stream, probably empty - ignore silently and abort
403                                 return;
404                         }
405                         
406                         CodeNamespace ns = new CodeNamespace (nsname);
407                         CodeTypeDeclaration cls = new CodeTypeDeclaration (classname);
408                         cls.IsClass = true;
409                         cls.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;
410
411                         CodeMemberField cmf = new CodeMemberField (typeof(CultureInfo), "culture");
412                         cmf.InitExpression = new CodePrimitiveExpression (null);
413                         cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
414                         cls.Members.Add (cmf);
415
416                         cmf = new CodeMemberField (typeof(ResourceManager), "resourceManager");
417                         cmf.InitExpression = new CodePrimitiveExpression (null);
418                         cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
419                         cls.Members.Add (cmf);
420                         
421                         // Property: ResourceManager
422                         CodeMemberProperty cmp = new CodeMemberProperty ();
423                         cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
424                         cmp.Name = "ResourceManager";
425                         cmp.HasGet = true;
426                         cmp.Type = new CodeTypeReference (typeof(ResourceManager));
427                         CodePropertyResourceManagerGet (cmp.GetStatements, resfile, classname);
428                         cls.Members.Add (cmp);
429
430                         // Property: Culture
431                         cmp = new CodeMemberProperty ();
432                         cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final;
433                         cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
434                         cmp.Name = "Culture";
435                         cmp.HasGet = true;
436                         cmp.HasSet = true;
437                         cmp.Type = new CodeTypeReference (typeof(CultureInfo));
438                         CodePropertyGenericGet (cmp.GetStatements, "culture", classname);
439                         CodePropertyGenericSet (cmp.SetStatements, "culture", classname);
440                         cls.Members.Add (cmp);
441
442                         // Add the resource properties
443                         Dictionary<string,bool> imports = new Dictionary<string,bool> ();
444                         try {
445                                 foreach (DictionaryEntry de in res) {
446                                         Type type = de.Value.GetType ();
447
448                                         if (!imports.ContainsKey (type.Namespace))
449                                                 imports [type.Namespace] = true;
450
451                                         string asname = new AssemblyName (type.Assembly.FullName).Name;
452                                         if (!assemblies.ContainsKey (asname))
453                                                 assemblies [asname] = true;
454                                         
455                                         cmp = new CodeMemberProperty ();
456                                         cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
457                                         cmp.Name = SanitizeResourceName ((string)de.Key);
458                                         cmp.HasGet = true;
459                                         CodePropertyResourceGet (cmp.GetStatements, (string)de.Key, type, classname);
460                                         cmp.Type = new CodeTypeReference (type);
461                                         cls.Members.Add (cmp);
462                                 }
463                         } catch (Exception ex) {
464                                 throw new ApplicationException ("Failed to compile global resources.", ex);
465                         }
466                         foreach (KeyValuePair<string,bool> de in imports)
467                                 ns.Imports.Add (new CodeNamespaceImport(de.Key));
468                         
469                         ns.Types.Add (cls);
470                         unit.Namespaces.Add (ns);
471                 }
472
473                 string SanitizeResourceName (string name)
474                 {
475                         return name.Replace (' ', '_').Replace ('-', '_').Replace ('.', '_');
476                 }
477                 
478                 CodeObjectCreateExpression NewResourceManager (string name, string typename)
479                 {
480                         CodeExpression resname = new CodePrimitiveExpression (name);
481                         CodePropertyReferenceExpression asm = new CodePropertyReferenceExpression (
482                                 new CodeTypeOfExpression (new CodeTypeReference (typename)),
483                                 "Assembly");
484                         
485                         return new CodeObjectCreateExpression ("System.Resources.ResourceManager",
486                                                                new CodeExpression [] {resname, asm});
487                 }
488                 
489                 void CodePropertyResourceManagerGet (CodeStatementCollection csc, string resfile, string typename)
490                 {
491                         string name = Path.GetFileNameWithoutExtension (resfile);
492                         CodeStatement st;
493                         CodeExpression exp;
494
495                         exp = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), "resourceManager");
496                         st = new CodeConditionStatement (
497                                 new CodeBinaryOperatorExpression (
498                                         exp,
499                                         CodeBinaryOperatorType.IdentityInequality,
500                                         new CodePrimitiveExpression (null)),
501                                 new CodeStatement [] { new CodeMethodReturnStatement (exp) });
502                         csc.Add (st);
503
504                         st = new CodeAssignStatement (exp, NewResourceManager (name, typename));
505                         csc.Add (st);
506                         csc.Add (new CodeMethodReturnStatement (exp));
507                 }
508
509                 void CodePropertyResourceGet (CodeStatementCollection csc, string resname, Type restype, string typename)
510                 {
511                         CodeStatement st = new CodeVariableDeclarationStatement (
512                                 typeof (ResourceManager),
513                                 "rm",
514                                 new CodePropertyReferenceExpression (
515                                         new CodeTypeReferenceExpression (typename), "ResourceManager"));
516                         csc.Add (st);
517
518                         st = new CodeConditionStatement (
519                                 new CodeBinaryOperatorExpression (
520                                         new CodeVariableReferenceExpression ("rm"),
521                                         CodeBinaryOperatorType.IdentityEquality,
522                                         new CodePrimitiveExpression (null)),
523                                 new CodeStatement [] { new CodeMethodReturnStatement (new CodePrimitiveExpression (null)) });
524                         csc.Add (st);
525
526                         bool gotstr = (restype == typeof (string));
527                         CodeExpression exp = new CodeMethodInvokeExpression (
528                                 new CodeVariableReferenceExpression ("rm"),
529                                 gotstr ? "GetString" : "GetObject",
530                                 new CodeExpression [] { new CodePrimitiveExpression (resname),
531                                                         new CodeFieldReferenceExpression (
532                                                                 new CodeTypeReferenceExpression (typename), "culture") });
533                         st = new CodeVariableDeclarationStatement (
534                                 restype,
535                                 "obj",
536                                 gotstr ? exp : new CodeCastExpression (restype, exp));
537                         csc.Add (st);
538                         csc.Add (new CodeMethodReturnStatement (new CodeVariableReferenceExpression ("obj")));
539                 }
540                 
541                 void CodePropertyGenericGet (CodeStatementCollection csc, string field, string typename)
542                 {
543                         csc.Add(new CodeMethodReturnStatement (
544                                         new CodeFieldReferenceExpression (
545                                                 new CodeTypeReferenceExpression (typename), field)));
546                 }
547
548                 void CodePropertyGenericSet (CodeStatementCollection csc, string field, string typename)
549                 {
550                         csc.Add(new CodeAssignStatement (
551                                         new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), field),
552                                         new CodeVariableReferenceExpression ("value")));
553                 }
554                 
555                 string CompileResource (AppResourceFileInfo arfi)
556                 {
557                         string path = arfi.Info.FullName;
558                         string resource = Path.Combine (TempDirectory,
559                                                         "Resources." + Path.GetFileNameWithoutExtension (path) + ".resources");
560                         FileStream source = null, destination = null;
561                         IResourceReader reader = null;
562                         ResourceWriter writer = null;
563
564                         try {
565                                 source = new FileStream (path, FileMode.Open, FileAccess.Read);
566                                 destination = new FileStream (resource, FileMode.Create, FileAccess.Write);
567                                 reader = GetReaderForKind (arfi.Kind, source);
568                                 writer = new ResourceWriter (destination);
569                                 foreach (DictionaryEntry de in reader) {
570                                         object val = de.Value;
571                                         if (val is string)
572                                                 writer.AddResource ((string)de.Key, (string)val);
573                                         else
574                                                 writer.AddResource ((string)de.Key, val);
575                                 }
576                         } catch (Exception ex) {
577                                 throw new HttpException ("Failed to compile resource file", ex);
578                         } finally {
579                                 if (reader != null)
580                                         reader.Close ();
581                                 else if (source != null)
582                                         source.Close ();
583                                 if (writer != null)
584                                         writer.Close ();
585                                 else if (destination != null)
586                                         destination.Close ();
587                         }
588                         
589                         return resource;
590                 }
591
592                 IResourceReader GetReaderForKind (AppResourceFileKind kind, Stream stream)
593                 {
594                         switch (kind) {
595                                 case AppResourceFileKind.ResX:
596                                         return new ResXResourceReader (stream);
597
598                                 case AppResourceFileKind.Resource:
599                                         return new ResourceReader (stream);
600
601                                 default:
602                                         return null;
603                         }
604                 }
605                 
606                                                                
607                 object OnCreateRandomFile (string path)
608                 {
609                         FileStream f = new FileStream (path, FileMode.CreateNew);
610                         f.Close ();
611                         return path;
612                 }
613         };
614 };
615 #endif