2 // System.Web.Compilation.AppResourceFilesCollection
5 // Marek Habersack (mhabersack@novell.com)
7 // (C) 2006 Marek Habersack
8 // (C) 2007-2009 Novell, Inc http://novell.com/
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.CodeDom.Compiler;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.ComponentModel.Design;
38 using System.Globalization;
40 using System.Reflection;
41 using System.Resources;
44 using System.Web.Caching;
45 using System.Web.Configuration;
46 using System.Web.Util;
48 namespace System.Web.Compilation
50 class AppResourcesCompiler
52 // This class fixes bug #466059
53 class TypeResolutionService : ITypeResolutionService
55 List <Assembly> referencedAssemblies;
56 Dictionary <string, Type> mappedTypes;
58 public Assembly GetAssembly (AssemblyName name)
60 return GetAssembly (name, false);
63 public Assembly GetAssembly (AssemblyName name, bool throwOnError)
66 return Assembly.Load (name);
75 public void ReferenceAssembly (AssemblyName name)
77 if (referencedAssemblies == null)
78 referencedAssemblies = new List <Assembly> ();
80 Assembly asm = GetAssembly (name, false);
84 if (referencedAssemblies.Contains (asm))
87 referencedAssemblies.Add (asm);
90 public string GetPathOfAssembly (AssemblyName name)
95 Assembly asm = GetAssembly (name, false);
102 public Type GetType (string name)
104 return GetType (name, false, false);
107 public Type GetType (string name, bool throwOnError)
109 return GetType (name, throwOnError, false);
112 public Type GetType (string name, bool throwOnError, bool ignoreCase)
114 if (String.IsNullOrEmpty (name)) {
116 throw new ArgumentNullException ("name");
121 int idx = name.IndexOf (',');
124 type = MapType (name, false);
128 type = FindInAssemblies (name, ignoreCase);
131 throw new InvalidOperationException ("Type '" + name + "' is not fully qualified and there are no referenced assemblies.");
139 type = MapType (name, true);
143 return Type.GetType (name, throwOnError, ignoreCase);
146 Type MapType (string name, bool full)
148 if (mappedTypes == null)
149 mappedTypes = new Dictionary <string, Type> (StringComparer.Ordinal);
152 if (mappedTypes.TryGetValue (name, out ret))
156 if (String.Compare (name, "ResXDataNode", StringComparison.Ordinal) == 0)
157 return AddMappedType (name, typeof (ResXDataNode));
158 if (String.Compare (name, "ResXFileRef", StringComparison.Ordinal) == 0)
159 return AddMappedType (name, typeof (ResXFileRef));
160 if (String.Compare (name, "ResXNullRef", StringComparison.Ordinal) == 0)
161 return AddMappedType (name, typeof (ResXNullRef));
162 if (String.Compare (name, "ResXResourceReader", StringComparison.Ordinal) == 0)
163 return AddMappedType (name, typeof (ResXResourceReader));
164 if (String.Compare (name, "ResXResourceWriter", StringComparison.Ordinal) == 0)
165 return AddMappedType (name, typeof (ResXResourceWriter));
170 if (name.IndexOf ("System.Windows.Forms") == -1)
173 if (name.IndexOf ("ResXDataNode", StringComparison.Ordinal) != -1)
174 return AddMappedType (name, typeof (ResXDataNode));
175 if (name.IndexOf ("ResXFileRef", StringComparison.Ordinal) != -1)
176 return AddMappedType (name, typeof (ResXFileRef));
177 if (name.IndexOf ("ResXNullRef", StringComparison.Ordinal) != -1)
178 return AddMappedType (name, typeof (ResXNullRef));
179 if (name.IndexOf ("ResXResourceReader", StringComparison.Ordinal) != -1)
180 return AddMappedType (name, typeof (ResXResourceReader));
181 if (name.IndexOf ("ResXResourceWriter", StringComparison.Ordinal) != -1)
182 return AddMappedType (name, typeof (ResXResourceWriter));
187 Type AddMappedType (string name, Type type)
189 mappedTypes.Add (name, type);
193 Type FindInAssemblies (string name, bool ignoreCase)
195 Type ret = Type.GetType (name, false);
199 if (referencedAssemblies == null || referencedAssemblies.Count == 0)
202 foreach (Assembly asm in referencedAssemblies) {
203 ret = asm.GetType (name, false, ignoreCase);
212 const string cachePrefix = "@@LocalResourcesAssemblies";
215 AppResourceFilesCollection files;
216 string tempDirectory;
218 Dictionary <string, List <string>> cultureFiles;
219 List <string> defaultCultureFiles;
221 string TempDirectory {
223 if (tempDirectory != null)
224 return tempDirectory;
225 return (tempDirectory = AppDomain.CurrentDomain.SetupInformation.DynamicBase);
229 public Dictionary <string, List <string>> CultureFiles {
230 get { return cultureFiles; }
233 public List <string> DefaultCultureFiles {
234 get { return defaultCultureFiles; }
237 static AppResourcesCompiler ()
239 if (!BuildManager.IsPrecompiled)
242 string[] binDirAssemblies = HttpApplication.BinDirectoryAssemblies;
243 if (binDirAssemblies == null || binDirAssemblies.Length == 0)
248 foreach (string asmPath in binDirAssemblies) {
249 if (String.IsNullOrEmpty (asmPath))
252 name = Path.GetFileName (asmPath);
253 if (name.StartsWith ("App_LocalResources.", StringComparison.OrdinalIgnoreCase)) {
254 string virtualPath = GetPrecompiledVirtualPath (asmPath);
255 if (String.IsNullOrEmpty (virtualPath))
258 asm = LoadAssembly (asmPath);
262 AddAssemblyToCache (virtualPath, asm);
266 if (String.Compare (name, "App_GlobalResources.dll", StringComparison.OrdinalIgnoreCase) != 0)
269 asm = LoadAssembly (asmPath);
273 HttpContext.AppGlobalResourcesAssembly = asm;
277 public AppResourcesCompiler (HttpContext context)
279 this.isGlobal = true;
280 this.files = new AppResourceFilesCollection (context);
281 this.cultureFiles = new Dictionary <string, List <string>> (StringComparer.OrdinalIgnoreCase);
284 public AppResourcesCompiler (string virtualPath)
287 this.virtualPath = virtualPath;
288 this.isGlobal = false;
289 this.files = new AppResourceFilesCollection (HttpContext.Current.Request.MapPath (virtualPath));
290 this.cultureFiles = new Dictionary <string, List <string>> (StringComparer.OrdinalIgnoreCase);
293 static Assembly LoadAssembly (string asmPath)
296 return Assembly.LoadFrom (asmPath);
297 } catch (BadImageFormatException) {
303 static string GetPrecompiledVirtualPath (string asmPath)
305 string compiledFile = Path.ChangeExtension (asmPath, ".compiled");
307 if (!File.Exists (compiledFile))
310 var pfile = new PreservationFile (compiledFile);
311 string virtualPath = pfile.VirtualPath;
312 if (String.IsNullOrEmpty (virtualPath))
315 if (virtualPath.EndsWith ("/App_LocalResources/", StringComparison.OrdinalIgnoreCase))
316 virtualPath = virtualPath.Substring (0, virtualPath.Length - 19);
321 public Assembly Compile ()
327 return CompileGlobal ();
329 return CompileLocal ();
332 Assembly CompileGlobal ()
334 string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
335 "App_GlobalResources",
337 OnCreateRandomFile) as string;
339 if (assemblyPath == null)
340 throw new ApplicationException ("Failed to create global resources assembly");
342 List <string>[] fileGroups = GroupGlobalFiles ();
343 if (fileGroups == null || fileGroups.Length == 0)
346 CodeCompileUnit unit = new CodeCompileUnit ();
347 CodeNamespace ns = new CodeNamespace (null);
348 ns.Imports.Add (new CodeNamespaceImport ("System"));
349 ns.Imports.Add (new CodeNamespaceImport ("System.Globalization"));
350 ns.Imports.Add (new CodeNamespaceImport ("System.Reflection"));
351 ns.Imports.Add (new CodeNamespaceImport ("System.Resources"));
352 unit.Namespaces.Add (ns);
354 AppResourcesAssemblyBuilder builder = new AppResourcesAssemblyBuilder ("App_GlobalResources", assemblyPath,
356 CodeDomProvider provider = builder.Provider;
358 Dictionary <string,bool> assemblies = new Dictionary<string,bool> ();
359 foreach (List<string> ls in fileGroups)
360 DomFromResource (ls [0], unit, assemblies, provider);
362 foreach (KeyValuePair<string,bool> de in assemblies)
363 unit.ReferencedAssemblies.Add (de.Key);
365 builder.Build (unit);
366 HttpContext.AppGlobalResourcesAssembly = builder.MainAssembly;
368 return builder.MainAssembly;
371 Assembly CompileLocal ()
373 if (String.IsNullOrEmpty (virtualPath))
376 Assembly cached = GetCachedLocalResourcesAssembly (virtualPath);
381 if (virtualPath == "/")
382 prefix = "App_LocalResources.root";
384 prefix = "App_LocalResources" + virtualPath.Replace ('/', '.');
386 string assemblyPath = FileUtils.CreateTemporaryFile (TempDirectory,
389 OnCreateRandomFile) as string;
390 if (assemblyPath == null)
391 throw new ApplicationException ("Failed to create local resources assembly");
393 List<AppResourceFileInfo> files = this.files.Files;
394 foreach (AppResourceFileInfo arfi in files)
395 GetResourceFile (arfi, true);
397 AppResourcesAssemblyBuilder builder = new AppResourcesAssemblyBuilder ("App_LocalResources", assemblyPath,
400 Assembly ret = builder.MainAssembly;
403 AddAssemblyToCache (virtualPath, ret);
408 internal static Assembly GetCachedLocalResourcesAssembly (string path)
410 Dictionary <string, Assembly> cache;
412 cache = HttpRuntime.InternalCache[cachePrefix] as Dictionary <string, Assembly>;
413 if (cache == null || !cache.ContainsKey (path))
418 static void AddAssemblyToCache (string path, Assembly asm)
420 Cache runtimeCache = HttpRuntime.InternalCache;
421 Dictionary <string, Assembly> cache;
423 cache = runtimeCache[cachePrefix] as Dictionary <string, Assembly>;
425 cache = new Dictionary <string, Assembly> ();
427 runtimeCache.Insert (cachePrefix, cache);
430 uint CountChars (char c, string s)
433 foreach (char ch in s) {
440 string IsFileCultureValid (string fileName)
442 string tmp = Path.GetFileNameWithoutExtension (fileName);
443 tmp = Path.GetExtension (tmp);
444 if (tmp != null && tmp.Length > 0) {
445 tmp = tmp.Substring (1);
447 CultureInfo.GetCultureInfo (tmp);
456 string GetResourceFile (AppResourceFileInfo arfi, bool local)
459 if (arfi.Kind == AppResourceFileKind.ResX)
460 resfile = CompileResource (arfi, local);
462 resfile = arfi.Info.FullName;
463 if (!String.IsNullOrEmpty (resfile)) {
464 string culture = IsFileCultureValid (resfile);
465 List <string> cfiles;
466 if (culture != null) {
467 if (cultureFiles.ContainsKey (culture))
468 cfiles = cultureFiles [culture];
470 cfiles = new List <string> (1);
471 cultureFiles [culture] = cfiles;
474 if (defaultCultureFiles == null)
475 defaultCultureFiles = new List <string> ();
476 cfiles = defaultCultureFiles;
479 cfiles.Add (resfile);
485 List <string>[] GroupGlobalFiles ()
487 List<AppResourceFileInfo> files = this.files.Files;
488 List<List<string>> groups = new List<List<string>> ();
489 AppResourcesLengthComparer<List<string>> lcList = new AppResourcesLengthComparer<List<string>> ();
491 string tmp, s, basename;
492 uint basedots, filedots;
493 AppResourceFileInfo defaultFile;
495 foreach (AppResourceFileInfo arfi in files) {
496 if (arfi.Kind != AppResourceFileKind.ResX && arfi.Kind != AppResourceFileKind.Resource)
499 s = arfi.Info.FullName;
500 basename = Path.GetFileNameWithoutExtension (s);
501 basedots = CountChars ('.', basename);
504 // If there are any files that start with this baseName, we have a default file
505 foreach (AppResourceFileInfo fi in files) {
509 string s2 = fi.Info.FullName;
510 if (s2 == null || s == s2)
512 tmp = Path.GetFileNameWithoutExtension (s2);
513 filedots = CountChars ('.', tmp);
515 if (filedots == basedots + 1 && tmp.StartsWith (basename)) {
516 if (IsFileCultureValid (s2) != null) {
517 // A valid translated file for this name
521 // This file shares the base name, but the culture is invalid - we must
522 // ignore it since the name of the generated strongly typed class for this
523 // resource will clash with the one generated from the default file with
524 // the given basename.
529 if (defaultFile != null) {
530 List<string> al = new List<string> ();
531 al.Add (GetResourceFile (arfi, false));
537 groups.Sort (lcList);
540 // Now find their translated counterparts
541 foreach (List<string> al in groups) {
543 tmp = Path.GetFileNameWithoutExtension (s);
544 if (tmp.StartsWith ("Resources."))
545 tmp = tmp.Substring (10);
546 foreach (AppResourceFileInfo arfi in files) {
549 s = arfi.Info.FullName;
552 tmp2 = arfi.Info.Name;
553 if (tmp2.StartsWith (tmp)) {
554 al.Add (GetResourceFile (arfi, false));
560 // Anything that's left here might be orphans or lone default files.
561 // For those files we check the part following the last dot
562 // before the .resx/.resource extensions and test whether it's a registered
563 // culture or not. If it is not a culture, then we have a
564 // default file that doesn't have any translations. Otherwise,
565 // the file is ignored (it's the same thing MS.NET does)
566 foreach (AppResourceFileInfo arfi in files) {
570 if (IsFileCultureValid (arfi.Info.FullName) != null)
571 continue; // Culture found, we reject the file
573 // A single default file, create a group
574 List<string> al = new List<string> ();
575 al.Add (GetResourceFile (arfi, false));
578 groups.Sort (lcList);
579 return groups.ToArray ();
582 // CodeDOM generation
583 void DomFromResource (string resfile, CodeCompileUnit unit, Dictionary <string,bool> assemblies,
584 CodeDomProvider provider)
586 if (String.IsNullOrEmpty (resfile))
589 string fname, nsname, classname;
591 fname = Path.GetFileNameWithoutExtension (resfile);
592 nsname = Path.GetFileNameWithoutExtension (fname);
593 classname = Path.GetExtension (fname);
594 if (classname == null || classname.Length == 0) {
596 nsname = "Resources";
598 if (!nsname.StartsWith ("Resources", StringComparison.InvariantCulture))
599 nsname = String.Concat ("Resources.", nsname);
600 classname = classname.Substring(1);
603 if (!String.IsNullOrEmpty (classname))
604 classname = classname.Replace ('.', '_');
605 if (!String.IsNullOrEmpty (nsname))
606 nsname = nsname.Replace ('.', '_');
608 if (!provider.IsValidIdentifier (nsname) || !provider.IsValidIdentifier (classname))
609 throw new ApplicationException ("Invalid resource file name.");
613 res = new ResourceReader (resfile);
614 } catch (ArgumentException) {
615 // invalid stream, probably empty - ignore silently and abort
619 CodeNamespace ns = new CodeNamespace (nsname);
620 CodeTypeDeclaration cls = new CodeTypeDeclaration (classname);
622 cls.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;
624 CodeMemberField cmf = new CodeMemberField (typeof(CultureInfo), "_culture");
625 cmf.InitExpression = new CodePrimitiveExpression (null);
626 cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
627 cls.Members.Add (cmf);
629 cmf = new CodeMemberField (typeof(ResourceManager), "_resourceManager");
630 cmf.InitExpression = new CodePrimitiveExpression (null);
631 cmf.Attributes = MemberAttributes.Private | MemberAttributes.Final | MemberAttributes.Static;
632 cls.Members.Add (cmf);
634 // Property: ResourceManager
635 CodeMemberProperty cmp = new CodeMemberProperty ();
636 cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
637 cmp.Name = "ResourceManager";
639 cmp.Type = new CodeTypeReference (typeof(ResourceManager));
640 CodePropertyResourceManagerGet (cmp.GetStatements, resfile, classname);
641 cls.Members.Add (cmp);
644 cmp = new CodeMemberProperty ();
645 cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final;
646 cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
647 cmp.Name = "Culture";
650 cmp.Type = new CodeTypeReference (typeof(CultureInfo));
651 CodePropertyGenericGet (cmp.GetStatements, "_culture", classname);
652 CodePropertyGenericSet (cmp.SetStatements, "_culture", classname);
653 cls.Members.Add (cmp);
655 // Add the resource properties
656 Dictionary<string,bool> imports = new Dictionary<string,bool> ();
658 foreach (DictionaryEntry de in res) {
659 Type type = de.Value.GetType ();
661 if (!imports.ContainsKey (type.Namespace))
662 imports [type.Namespace] = true;
664 string asname = new AssemblyName (type.Assembly.FullName).Name;
665 if (!assemblies.ContainsKey (asname))
666 assemblies [asname] = true;
668 cmp = new CodeMemberProperty ();
669 cmp.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
670 cmp.Name = SanitizeResourceName (provider, (string)de.Key);
672 CodePropertyResourceGet (cmp.GetStatements, (string)de.Key, type, classname);
673 cmp.Type = new CodeTypeReference (type);
674 cls.Members.Add (cmp);
676 } catch (Exception ex) {
677 throw new ApplicationException ("Failed to compile global resources.", ex);
679 foreach (KeyValuePair<string,bool> de in imports)
680 ns.Imports.Add (new CodeNamespaceImport(de.Key));
683 unit.Namespaces.Add (ns);
686 static bool is_identifier_start_character (int c)
688 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter ((char)c);
691 static bool is_identifier_part_character (char c)
693 if (c >= 'a' && c <= 'z')
696 if (c >= 'A' && c <= 'Z')
699 if (c == '_' || (c >= '0' && c <= '9'))
705 return Char.IsLetter (c) || Char.GetUnicodeCategory (c) == UnicodeCategory.ConnectorPunctuation;
708 string SanitizeResourceName (CodeDomProvider provider, string name)
710 if (provider.IsValidIdentifier (name))
711 return provider.CreateEscapedIdentifier (name);
713 var sb = new StringBuilder ();
715 if (is_identifier_start_character (ch))
719 if (ch >= '0' && ch <= '9')
723 for (int i = 1; i < name.Length; i++) {
725 if (is_identifier_part_character (ch))
731 return provider.CreateEscapedIdentifier (sb.ToString ());
734 CodeObjectCreateExpression NewResourceManager (string name, string typename)
736 CodeExpression resname = new CodePrimitiveExpression (name);
737 CodePropertyReferenceExpression asm = new CodePropertyReferenceExpression (
738 new CodeTypeOfExpression (new CodeTypeReference (typename)),
741 return new CodeObjectCreateExpression ("System.Resources.ResourceManager",
742 new CodeExpression [] {resname, asm});
745 void CodePropertyResourceManagerGet (CodeStatementCollection csc, string resfile, string typename)
747 string name = Path.GetFileNameWithoutExtension (resfile);
751 exp = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), "_resourceManager");
752 st = new CodeConditionStatement (
753 new CodeBinaryOperatorExpression (
755 CodeBinaryOperatorType.IdentityInequality,
756 new CodePrimitiveExpression (null)),
757 new CodeStatement [] { new CodeMethodReturnStatement (exp) });
760 st = new CodeAssignStatement (exp, NewResourceManager (name, typename));
762 csc.Add (new CodeMethodReturnStatement (exp));
765 void CodePropertyResourceGet (CodeStatementCollection csc, string resname, Type restype, string typename)
767 CodeStatement st = new CodeVariableDeclarationStatement (
768 typeof (ResourceManager),
770 new CodePropertyReferenceExpression (
771 new CodeTypeReferenceExpression (typename), "ResourceManager"));
774 st = new CodeConditionStatement (
775 new CodeBinaryOperatorExpression (
776 new CodeVariableReferenceExpression ("rm"),
777 CodeBinaryOperatorType.IdentityEquality,
778 new CodePrimitiveExpression (null)),
779 new CodeStatement [] { new CodeMethodReturnStatement (new CodePrimitiveExpression (null)) });
782 bool gotstr = (restype == typeof (string));
783 CodeExpression exp = new CodeMethodInvokeExpression (
784 new CodeVariableReferenceExpression ("rm"),
785 gotstr ? "GetString" : "GetObject",
786 new CodeExpression [] { new CodePrimitiveExpression (resname),
787 new CodeFieldReferenceExpression (
788 new CodeTypeReferenceExpression (typename), "_culture") });
789 st = new CodeVariableDeclarationStatement (
792 gotstr ? exp : new CodeCastExpression (restype, exp));
794 csc.Add (new CodeMethodReturnStatement (new CodeVariableReferenceExpression ("obj")));
797 void CodePropertyGenericGet (CodeStatementCollection csc, string field, string typename)
799 csc.Add(new CodeMethodReturnStatement (
800 new CodeFieldReferenceExpression (
801 new CodeTypeReferenceExpression (typename), field)));
804 void CodePropertyGenericSet (CodeStatementCollection csc, string field, string typename)
806 csc.Add(new CodeAssignStatement (
807 new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typename), field),
808 new CodeVariableReferenceExpression ("value")));
811 string CompileResource (AppResourceFileInfo arfi, bool local)
813 string path = arfi.Info.FullName;
814 string rname = Path.GetFileNameWithoutExtension (path) + ".resources";
816 rname = "Resources." + rname;
818 string resource = Path.Combine (TempDirectory, rname);
819 FileStream source = null, destination = null;
820 IResourceReader reader = null;
821 ResourceWriter writer = null;
824 source = new FileStream (path, FileMode.Open, FileAccess.Read);
825 destination = new FileStream (resource, FileMode.Create, FileAccess.Write);
826 reader = GetReaderForKind (arfi.Kind, source, path);
827 writer = new ResourceWriter (destination);
828 foreach (DictionaryEntry de in reader) {
829 object val = de.Value;
831 writer.AddResource ((string)de.Key, (string)val);
833 writer.AddResource ((string)de.Key, val);
835 } catch (Exception ex) {
836 throw new HttpException ("Failed to compile resource file", ex);
844 if (destination != null)
845 destination.Dispose ();
851 IResourceReader GetReaderForKind (AppResourceFileKind kind, Stream stream, string path)
854 case AppResourceFileKind.ResX:
855 var ret = new ResXResourceReader (stream, new TypeResolutionService ());
856 if (!String.IsNullOrEmpty (path))
857 ret.BasePath = Path.GetDirectoryName (path);
860 case AppResourceFileKind.Resource:
861 return new ResourceReader (stream);
869 object OnCreateRandomFile (string path)
871 FileStream f = new FileStream (path, FileMode.CreateNew);