Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed
[mono.git] / mcs / class / referencesource / mscorlib / system / resources / filebasedresourcegroveler.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  FileBasedResourceGroveler
9 ** 
10 ** <OWNER>[....]</OWNER>
11 **
12 **
13 ** Purpose: Searches for resources on disk, used for file-
14 ** based resource lookup.
15 **
16 ** 
17 ===========================================================*/
18 namespace System.Resources {    
19     using System;
20     using System.Collections;
21     using System.Collections.Generic;
22     using System.IO;
23     using System.Globalization;
24     using System.Runtime.CompilerServices;
25     using System.Runtime.Versioning;
26     using System.Text;
27     using System.Threading;
28     using System.Diagnostics.Contracts;
29
30     internal class FileBasedResourceGroveler : IResourceGroveler
31     {
32         private ResourceManager.ResourceManagerMediator _mediator;
33
34         public FileBasedResourceGroveler(ResourceManager.ResourceManagerMediator mediator)
35         {
36             Contract.Assert(mediator != null, "mediator shouldn't be null; check caller");
37             _mediator = mediator;
38         }
39
40         // Consider modifying IResourceGroveler interface (hence this method signature) when we figure out 
41         // serialization compat story for moving ResourceManager members to either file-based or 
42         // manifest-based classes. Want to continue tightening the design to get rid of unused params.
43         [System.Security.SecuritySafeCritical]  // auto-generated
44         public ResourceSet GrovelForResourceSet(CultureInfo culture, Dictionary<String, ResourceSet> localResourceSets, bool tryParents, bool createIfNotExists, ref StackCrawlMark stackMark) 
45         {
46             Contract.Assert(culture != null, "culture shouldn't be null; check caller");
47
48             String fileName = null;
49             ResourceSet rs = null;
50
51             // Don't use Assembly manifest, but grovel on disk for a file.
52             try
53             {
54 #if MONO_FEATURE_CAS
55                 new System.Security.Permissions.FileIOPermission(System.Security.Permissions.PermissionState.Unrestricted).Assert();
56 #endif
57
58                 // Create new ResourceSet, if a file exists on disk for it.
59                 String tempFileName = _mediator.GetResourceFileName(culture);
60                 fileName = FindResourceFile(culture, tempFileName);
61                 if (fileName == null)
62                 {
63                     if (tryParents)
64                     {
65                         // If we've hit top of the Culture tree, return.
66                         if (culture.HasInvariantCultureName)
67                         {
68                             // We really don't think this should happen - we always
69                             // expect the neutral locale's resources to be present.
70                             throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoNeutralDisk") + Environment.NewLine + "baseName: " + _mediator.BaseNameField + "  locationInfo: " + (_mediator.LocationInfo == null ? "<null>" : _mediator.LocationInfo.FullName) + "  fileName: " + _mediator.GetResourceFileName(culture));
71                         }
72                     }
73                 }
74                 else
75                 {
76                     rs = CreateResourceSet(fileName);
77                 }
78                 return rs;
79             }
80             finally
81             {
82 #if MONO_FEATURE_CAS
83                 System.Security.CodeAccessPermission.RevertAssert();
84 #endif
85             }
86         }
87
88 #if !FEATURE_CORECLR   // PAL doesn't support eventing, and we don't compile event providers for coreclr
89         public bool HasNeutralResources(CultureInfo culture, String defaultResName)
90         {
91             // Detect missing neutral locale resources.
92             String defaultResPath = FindResourceFile(culture, defaultResName);
93             if (defaultResPath == null || !File.Exists(defaultResPath))
94             {
95                 String dir = _mediator.ModuleDir;
96                 if (defaultResPath != null)
97                 {
98                     dir = Path.GetDirectoryName(defaultResPath);
99                 }
100                 return false;
101             }
102             return true;
103         }
104 #endif
105
106         // Given a CultureInfo, it generates the path &; file name for 
107         // the .resources file for that CultureInfo.  This method will grovel
108         // the disk looking for the correct file name & path.  Uses CultureInfo's
109         // Name property.  If the module directory was set in the ResourceManager 
110         // constructor, we'll look there first.  If it couldn't be found in the module
111         // diretory or the module dir wasn't provided, look in the current
112         // directory.
113
114         [ResourceExposure(ResourceScope.Machine)]
115         [ResourceConsumption(ResourceScope.Machine)]
116         private String FindResourceFile(CultureInfo culture, String fileName)
117         {
118             Contract.Assert(culture != null, "culture shouldn't be null; check caller");
119             Contract.Assert(fileName != null, "fileName shouldn't be null; check caller");
120
121             // If we have a moduleDir, check there first.  Get module fully 
122             // qualified name, append path to that.
123             if (_mediator.ModuleDir != null)
124             {
125 #if _DEBUG
126                 if (ResourceManager.DEBUG >= 3)
127                     BCLDebug.Log("FindResourceFile: checking module dir: \""+_mediator.ModuleDir+'\"');
128 #endif
129
130                 String path = Path.Combine(_mediator.ModuleDir, fileName);
131                 if (File.Exists(path))
132                 {
133 #if _DEBUG
134                     if (ResourceManager.DEBUG >= 3)
135                         BCLDebug.Log("Found resource file in module dir!  "+path);
136 #endif
137                     return path;
138                 }
139             }
140
141 #if _DEBUG
142             if (ResourceManager.DEBUG >= 3)
143                 BCLDebug.Log("Couldn't find resource file in module dir, checking .\\"+fileName);
144 #endif
145
146             // look in .
147             if (File.Exists(fileName))
148                 return fileName;
149
150             return null;  // give up.
151         }
152
153         // Constructs a new ResourceSet for a given file name.  The logic in
154         // here avoids a ReflectionPermission check for our RuntimeResourceSet
155         // for perf and working set reasons.
156         [System.Security.SecurityCritical]
157         [ResourceExposure(ResourceScope.Machine)]
158         [ResourceConsumption(ResourceScope.Machine)]
159         private ResourceSet CreateResourceSet(String file)
160         {
161             Contract.Assert(file != null, "file shouldn't be null; check caller");
162
163             if (_mediator.UserResourceSet == null)
164             {
165                 // Explicitly avoid CreateInstance if possible, because it
166                 // requires ReflectionPermission to call private & protected
167                 // constructors.  
168                 return new RuntimeResourceSet(file);
169             }
170             else
171             {
172                 Object[] args = new Object[1];
173                 args[0] = file;
174                 try
175                 {
176                     return (ResourceSet)Activator.CreateInstance(_mediator.UserResourceSet, args);
177                 }
178                 catch (MissingMethodException e)
179                 {
180                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResMgrBadResSet_Type", _mediator.UserResourceSet.AssemblyQualifiedName), e);
181                 }
182             }
183         }
184     }
185 }