// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
/*============================================================
**
** Class:  ResourceManager
** 
** <OWNER>jathaine</OWNER>
**
**
** Purpose: Default way to access String and Object resources
** from an assembly.
**
** 
===========================================================*/

namespace System.Resources {    
    using System;
    using System.IO;
    using System.Globalization;
    using System.Collections;
    using System.Text;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.Security;
    using System.Security.Permissions;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Runtime.CompilerServices;
    using Microsoft.Win32;
    using System.Collections.Generic;
    using System.Runtime.Versioning;
    using System.Diagnostics.Contracts;
#if !FEATURE_CORECLR
    using System.Diagnostics.Tracing;
#endif

#if FEATURE_APPX
    //
    // This is implemented in System.Runtime.WindowsRuntime as function System.Resources.WindowsRuntimeResourceManager,
    // allowing us to ask for a WinRT-specific ResourceManager.
    // It is important to have WindowsRuntimeResourceManagerBase as regular class with virtual methods and default implementations. 
    // Defining WindowsRuntimeResourceManagerBase as abstract class or interface will cause issues when adding more methods to it 
    // because it�ll create dependency between mscorlib and System.Runtime.WindowsRuntime which will require always shipping both DLLs together. 
    // Also using interface or abstract class will not play nice with FriendAccessAllowed.
    //
    [FriendAccessAllowed]
    [SecurityCritical]
    internal class WindowsRuntimeResourceManagerBase
    {
        [SecurityCritical]
        public virtual bool Initialize(string libpath, string reswFilename, out PRIExceptionInfo exceptionInfo){exceptionInfo = null; return false;}

        [SecurityCritical]
        public virtual String GetString(String stringName, String startingCulture, String neutralResourcesCulture){return null;}

        public virtual CultureInfo GlobalResourceContextBestFitCultureInfo { 
            [SecurityCritical]
            get { return null; } 
        }
        
        [SecurityCritical]
        public virtual bool SetGlobalResourceContextDefaultCulture(CultureInfo ci) { return false; }
    }

    [FriendAccessAllowed]
    // [[....] 3/9/2012] This class should be named PRIErrorInfo.
    //
    // During Dev11 CLR RC Ask mode, the Windows Modern Resource Manager
    // made a breaking change such that ResourceMap.GetSubtree returns null when a subtree is
    // not found instead of throwing an exception. As a result the name of this class is no longer accurate.
    // It should be called PRIErrorInfo. However changing the name of this internal class would cause
    // mscorlib.asmmeta and System.Runtime.WindowsRuntime.asmmeta to change,
    // which would in turn require updating of the mscorlib and System.Runtime.WindowsRuntime
    // reference assemblies under InternalApis. This would not meet the Ask Mode bar at this time.
    // To get an idea of which files may need to be updated when updating this name,
    // see changeset 399234 in the DevDiv2 database, though the update procedure may have changed
    // by the time you read this.
    internal class PRIExceptionInfo
    {
        public string _PackageSimpleName;
        public string _ResWFile;
    }
#endif // FEATURE_APPX

    // Resource Manager exposes an assembly's resources to an application for
    // the correct CultureInfo.  An example would be localizing text for a 
    // user-visible message.  Create a set of resource files listing a name 
    // for a message and its value, compile them using ResGen, put them in
    // an appropriate place (your assembly manifest(?)), then create a Resource 
    // Manager and query for the name of the message you want.  The Resource
    // Manager will use CultureInfo.GetCurrentUICulture() to look
    // up a resource for your user's locale settings.
    // 
    // Users should ideally create a resource file for every culture, or
    // at least a meaningful subset.  The filenames will follow the naming 
    // scheme:
    // 
    // basename.culture name.resources
    // 
    // The base name can be the name of your application, or depending on 
    // the granularity desired, possibly the name of each class.  The culture 
    // name is determined from CultureInfo's Name property.  
    // An example file name may be MyApp.en-US.resources for
    // MyApp's US English resources.
    // 
    // -----------------
    // Refactoring Notes
    // -----------------
    // In Feb 08, began first step of refactoring ResourceManager to improve
    // maintainability (sd changelist 3012100). This resulted in breaking
    // apart the InternalGetResourceSet "big loop" so that the file-based
    // and manifest-based lookup was located in separate methods. 
    // In Apr 08, continued refactoring so that file-based and manifest-based
    // concerns are encapsulated by separate classes. At construction, the
    // ResourceManager creates one of these classes based on whether the 
    // RM will need to use file-based or manifest-based resources, and 
    // afterwards refers to this through the interface IResourceGroveler.
    // 
    // Serialization Compat: Ideally, we could have refactored further but
    // this would have broken serialization compat. For example, the
    // ResourceManager member UseManifest and UseSatelliteAssem are no 
    // longer relevant on ResourceManager. Similarly, other members could
    // ideally be moved to the file-based or manifest-based classes 
    // because they are only relevant for those types of lookup.
    //
    // Solution now / in the future: 
    // For now, we simply use a mediator class so that we can keep these
    // members on ResourceManager but allow the file-based and manifest-
    // based classes to access/set these members in a uniform way. See
    // ResourceManagerMediator.
    // We encapsulate fallback logic in a fallback iterator class, so that 
    // this logic isn't duplicated in several methods.
    // 
    // In the future, we can look into either breaking serialization if we
    // decide this doesn't make sense for ResourceManager (i.e. how common
    // is the scenario), manually make serialization work by providing 
    // appropriate OnSerialization, Deserialization methods. We can also 
    // look into further factoring and better design of IResourceGroveler
    // interface to accommodate unused parameters that don't make sense
    // for either file-based or manifest-based lookup paths.
    //
    // Benefits of this refactoring:
    // - Makes it possible to understand what the ResourceManager does, 
    // which is key for maintainability. 
    // - Makes the ResourceManager more extensible by identifying and
    // encapsulating what varies
    // - Unearthed a bug that's been lurking a while in file-based 
    // lookup paths for InternalGetResourceSet if createIfNotExists is
    // false.
    // - Reuses logic, e.g. by breaking apart the culture fallback into 
    // the fallback iterator class, we don't have to repeat the 
    // sometimes confusing fallback logic across multiple methods
    // - Fxcop violations reduced to 1/5th of original count. Most 
    // importantly, code complexity violations disappeared.
    // - Finally, it got rid of dead code paths. Because the big loop was
    // so confusing, it masked unused chunks of code. Also, dividing 
    // between file-based and manifest-based allowed functionaliy 
    // unused in silverlight to fall out.
    // 
    // Note: this type is integral to the construction of exception objects,
    // and sometimes this has to be done in low memory situtations (OOM) or
    // to create TypeInitializationExceptions due to failure of a static class
    // constructor. This type needs to be extremely careful and assume that 
    // any type it references may have previously failed to construct, so statics
    // belonging to that type may not be initialized. FrameworkEventSource.Log
    // is one such example.
    //

    [Serializable]
    [System.Runtime.InteropServices.ComVisible(true)]
    public class ResourceManager
    {

        internal class CultureNameResourceSetPair {
            public String lastCultureName;
            public ResourceSet lastResourceSet;
        }

        protected String BaseNameField;
        // Sets is a many-to-one table of CultureInfos mapped to ResourceSets.
        // Don't synchronize ResourceSets - too fine-grained a lock to be effective
        [Obsolete("call InternalGetResourceSet instead")]
        protected Hashtable ResourceSets;
        

        // don't serialize the cache of ResourceSets
        [NonSerialized]
        private Dictionary <String,ResourceSet> _resourceSets;
        private String moduleDir;      // For assembly-ignorant directory location
        protected Assembly MainAssembly;   // Need the assembly manifest sometimes.
        private Type _locationInfo;    // For Assembly or type-based directory layout
        private Type _userResourceSet;  // Which ResourceSet instance to create
        private CultureInfo _neutralResourcesCulture;  // For perf optimizations.

        [NonSerialized]
        private CultureNameResourceSetPair _lastUsedResourceCache;

        private bool _ignoreCase;   // Whether case matters in GetString & GetObject

        private bool UseManifest;  // Use Assembly manifest, or grovel disk.

        // unused! But need to keep for serialization
        [OptionalField(VersionAdded = 1)]
        private bool UseSatelliteAssem;  // Are all the .resources files in the 
                  // main assembly, or in satellite assemblies for each culture?
#if RESOURCE_SATELLITE_CONFIG
        private static volatile Hashtable _installedSatelliteInfo;  // Give the user the option  
               // to prevent certain satellite assembly probes via a config file.
        // Note that config files are per-appdomain, not per-assembly nor process
        private static volatile bool _checkedConfigFile;  // Did we read the app's config file?
#endif

        // Whether to fall back to the main assembly or a particular 
        // satellite for the neutral resources.
        [OptionalField]
        private UltimateResourceFallbackLocation _fallbackLoc;
        // Version number of satellite assemblies to look for.  May be null.
        [OptionalField]
        private Version _satelliteContractVersion;
        [OptionalField]
        private bool _lookedForSatelliteContractVersion;

        // unused! But need to keep for serialization
        [OptionalField(VersionAdded = 1)]
        private Assembly _callingAssembly;  // Assembly who created the ResMgr.

        // replaces _callingAssembly
        [OptionalField(VersionAdded = 4)]
        private RuntimeAssembly m_callingAssembly;  // Assembly who created the ResMgr.

        // no need to serialize this; just create a new one on deserialization
        [NonSerialized]
        private IResourceGroveler resourceGroveler;

        public static readonly int MagicNumber = unchecked((int)0xBEEFCACE);  // If only hex had a K...

        // Version number so ResMgr can get the ideal set of classes for you.
        // ResMgr header is:
        // 1) MagicNumber (little endian Int32)
        // 2) HeaderVersionNumber (little endian Int32)
        // 3) Num Bytes to skip past ResMgr header (little endian Int32)
        // 4) IResourceReader type name for this file (bytelength-prefixed UTF-8 String)
        // 5) ResourceSet type name for this file (bytelength-prefixed UTF8 String)
        public static readonly int HeaderVersionNumber = 1;

        //
        //It would be better if we could use _neutralCulture instead of calling
        //CultureInfo.InvariantCulture everywhere, but we run into problems with the .cctor.  CultureInfo 
        //initializes assembly, which initializes ResourceManager, which tries to get a CultureInfo which isn't
        //there yet because CultureInfo's class initializer hasn't finished.  If we move SystemResMgr off of
        //Assembly (or at least make it an internal property) we should be able to circumvent this problem.
        //
        //      private static CultureInfo _neutralCulture = null;

        // This is our min required ResourceSet type.
        private static readonly Type _minResourceSet = typeof(ResourceSet);
        // These Strings are used to avoid using Reflection in CreateResourceSet.
        // The first set are used by ResourceWriter.  The second are used by
        // InternalResGen.
        internal static readonly String ResReaderTypeName = typeof(ResourceReader).FullName;
        internal static readonly String ResSetTypeName = typeof(RuntimeResourceSet).FullName;
        internal static readonly String MscorlibName = typeof(ResourceReader).Assembly.FullName;
        internal const String ResFileExtension = ".resources";
        internal const int ResFileExtensionLength = 10;

        // My private debugging aid.  Set to 5 or 6 for verbose output.  Set to 3
        // for summary level information.
        internal static readonly int DEBUG = 0; //Making this const causes C# to consider all of the code that it guards unreachable.
        
        private static volatile bool s_IsAppXModel;
        
        [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
        private void Init()
        {
            m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
        }

        protected ResourceManager() 
        {
#if !FEATURE_CORECLR
            // This constructor is not designed to be used under AppX and is not in the Win8 profile.
            // However designers may use them even if they are running under AppX since they are
            // not subject to the restrictions of the Win8 profile.
            Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
#endif

            Init();

            _lastUsedResourceCache = new CultureNameResourceSetPair();
            ResourceManagerMediator mediator = new ResourceManagerMediator(this);
            resourceGroveler = new ManifestBasedResourceGroveler(mediator);
        }
	
        // Constructs a Resource Manager for files beginning with 
        // baseName in the directory specified by resourceDir
        // or in the current directory.  This Assembly-ignorant constructor is 
        // mostly useful for testing your own ResourceSet implementation.
        //
        // A good example of a baseName might be "Strings".  BaseName 
        // should not end in ".resources".
        //
        // Note: System.Windows.Forms uses this method at design time.
        // 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private ResourceManager(String baseName, String resourceDir, Type usingResourceSet) {
            if (null==baseName)
                throw new ArgumentNullException("baseName");
            if (null==resourceDir)
                throw new ArgumentNullException("resourceDir");
            Contract.EndContractBlock();

#if !FEATURE_CORECLR
            // This constructor is not designed to be used under AppX and is not in the Win8 profile.
            // However designers may use them even if they are running under AppX since they are
            // not subject to the restrictions of the Win8 profile.
            Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
#endif

            BaseNameField = baseName;

            moduleDir = resourceDir;
            _userResourceSet = usingResourceSet;
#pragma warning disable 618
            ResourceSets = new Hashtable(); // for backward compatibility
#pragma warning restore 618
            _resourceSets = new Dictionary<String, ResourceSet>();
            _lastUsedResourceCache = new CultureNameResourceSetPair();
            UseManifest = false;

            ResourceManagerMediator mediator = new ResourceManagerMediator(this);
            resourceGroveler = new FileBasedResourceGroveler(mediator);

#if !FEATURE_CORECLR && !MONO  // PAL doesn't support eventing, and we don't compile event providers for coreclr
            if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
                CultureInfo culture = CultureInfo.InvariantCulture;
                String defaultResName = GetResourceFileName(culture);

                if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
                    FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
                }
                else {
                    FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, defaultResName);
                }
            }           
#endif
        }
    
        [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
        public ResourceManager(String baseName, Assembly assembly)
        {
            if (null==baseName)
                throw new ArgumentNullException("baseName");

            if (null==assembly)
                throw new ArgumentNullException("assembly");
            Contract.EndContractBlock();

            if (!(assembly is RuntimeAssembly))
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));

            MainAssembly = assembly;
            BaseNameField = baseName;

            SetAppXConfiguration();

            CommonAssemblyInit();

            m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
            // Special case for mscorlib - protect mscorlib's private resources.
            // This isn't for security reasons, but to ensure we can make
            // breaking changes to mscorlib's internal resources without 
            // assuming users may have taken a dependency on them.
            if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
            {
                m_callingAssembly = null;
            }
        }

        [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
        public ResourceManager(String baseName, Assembly assembly, Type usingResourceSet)
        {
            if (null==baseName)
                throw new ArgumentNullException("baseName");
            if (null==assembly)
                throw new ArgumentNullException("assembly");
            Contract.EndContractBlock();

#if !FEATURE_CORECLR
            // This constructor is not designed to be used under AppX and is not in the Win8 profile.
            // However designers may use them even if they are running under AppX since they are
            // not subject to the restrictions of the Win8 profile.
            Contract.Assert(!AppDomain.IsAppXModel() || AppDomain.IsAppXDesignMode());
#endif

            if (!(assembly is RuntimeAssembly))
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeAssembly"));

            MainAssembly = assembly;
            BaseNameField = baseName;
    
            if (usingResourceSet != null && (usingResourceSet != _minResourceSet) && !(usingResourceSet.IsSubclassOf(_minResourceSet)))
                throw new ArgumentException(Environment.GetResourceString("Arg_ResMgrNotResSet"), "usingResourceSet");
            _userResourceSet = usingResourceSet;

            CommonAssemblyInit();
            m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
            // Special case for mscorlib - protect mscorlib's private resources.
            // This isn't for security reasons, but to ensure we can make
            // breaking changes to mscorlib's internal resources without 
            // assuming users may have taken a dependency on them.
            if (assembly == typeof(Object).Assembly && m_callingAssembly != assembly)
                m_callingAssembly = null;
        }
        
        [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
        public ResourceManager(Type resourceSource)
        {
            if (null==resourceSource)
                throw new ArgumentNullException("resourceSource");
            Contract.EndContractBlock();

            if (!(resourceSource is RuntimeType))
                throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"));

            _locationInfo = resourceSource;
            MainAssembly = _locationInfo.Assembly;
            BaseNameField = resourceSource.Name;
            
            SetAppXConfiguration();

            CommonAssemblyInit();

            m_callingAssembly = (RuntimeAssembly)Assembly.GetCallingAssembly();
            // Special case for mscorlib - protect mscorlib's private resources.
            if (MainAssembly == typeof(Object).Assembly && m_callingAssembly != MainAssembly)
            {
                m_callingAssembly = null;
            }
        }

        [OnDeserializing]
        private void OnDeserializing(StreamingContext ctx)
        {
            this._resourceSets = null;
            this.resourceGroveler = null;
            this._lastUsedResourceCache = null;
        }

        [System.Security.SecuritySafeCritical]
        [OnDeserialized]
        private void OnDeserialized(StreamingContext ctx)
        {
            _resourceSets = new Dictionary<String, ResourceSet>();
            _lastUsedResourceCache = new CultureNameResourceSetPair();
            // set up resource groveler, depending on whether this ResourceManager
            // is looking for files or assemblies
            ResourceManagerMediator mediator = new ResourceManagerMediator(this);
            if (UseManifest)
            {
                resourceGroveler = new ManifestBasedResourceGroveler(mediator);
            }
            else
            {
                resourceGroveler = new FileBasedResourceGroveler(mediator);
            }

            // correct callingAssembly for v2
            if (this.m_callingAssembly == null)
            {
                this.m_callingAssembly = (RuntimeAssembly)_callingAssembly;
            }

            // v2 does this lazily
            if (UseManifest && this._neutralResourcesCulture == null)
            {
                _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);
            }
        }

        [OnSerializing]
        private void OnSerializing(StreamingContext ctx)
        {
            // Initialize the fields Whidbey expects
            _callingAssembly = m_callingAssembly;
            UseSatelliteAssem = UseManifest;
#pragma warning disable 618
            ResourceSets = new Hashtable(); // for backward compatibility
#pragma warning restore 618
        }
      

        // Trying to unify code as much as possible, even though having to do a
        // security check in each constructor prevents it.
        [System.Security.SecuritySafeCritical]
        private void CommonAssemblyInit()
        {
            if (_bUsingModernResourceManagement == false)
            {
                UseManifest = true;
        
                _resourceSets = new Dictionary<String,ResourceSet>();
                _lastUsedResourceCache = new CultureNameResourceSetPair();

                _fallbackLoc = UltimateResourceFallbackLocation.MainAssembly;

                ResourceManagerMediator mediator = new ResourceManagerMediator(this);
                resourceGroveler = new ManifestBasedResourceGroveler(mediator);
            }

            _neutralResourcesCulture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(MainAssembly, ref _fallbackLoc);

#if !FEATURE_CORECLR   // PAL doesn't support eventing, and we don't compile event providers for coreclr
            if (_bUsingModernResourceManagement == false)
            {
#if !MONO
                if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
                    CultureInfo culture = CultureInfo.InvariantCulture;
                    String defaultResName = GetResourceFileName(culture);

                    if (resourceGroveler.HasNeutralResources(culture, defaultResName)) {
                        FrameworkEventSource.Log.ResourceManagerNeutralResourcesFound(BaseNameField, MainAssembly, defaultResName);
                    }
                    else {
                        String outputResName = defaultResName;
                        if (_locationInfo != null && _locationInfo.Namespace != null)
                            outputResName = _locationInfo.Namespace + Type.Delimiter + defaultResName;
                        FrameworkEventSource.Log.ResourceManagerNeutralResourcesNotFound(BaseNameField, MainAssembly, outputResName);
                    }
                }
#endif
#pragma warning disable 618
                ResourceSets = new Hashtable(); // for backward compatibility
#pragma warning restore 618
            }
#endif
        }

        // Gets the base name for the ResourceManager.
        public virtual String BaseName {
            get { return BaseNameField; }
        }
    
        // Whether we should ignore the capitalization of resources when calling
        // GetString or GetObject.
        public virtual bool IgnoreCase {
            get { return _ignoreCase; }
            set { _ignoreCase = value; }
        }

        // Returns the Type of the ResourceSet the ResourceManager uses
        // to construct ResourceSets.
        public virtual Type ResourceSetType {
            get { return (_userResourceSet == null) ? typeof(RuntimeResourceSet) : _userResourceSet; }
        }

        protected UltimateResourceFallbackLocation FallbackLocation
        {
            get { return _fallbackLoc; }
            set { _fallbackLoc = value; }
        }

        // Tells the ResourceManager to call Close on all ResourceSets and 
        // release all resources.  This will shrink your working set by
        // potentially a substantial amount in a running application.  Any
        // future resource lookups on this ResourceManager will be as 
        // expensive as the very first lookup, since it will need to search
        // for files and load resources again.
        // 
        // This may be useful in some complex threading scenarios, where 
        // creating a new ResourceManager isn't quite the correct behavior.
        public virtual void ReleaseAllResources()
        {
#if !FEATURE_CORECLR && !MONO
            if (FrameworkEventSource.IsInitialized)
            {
                FrameworkEventSource.Log.ResourceManagerReleasingResources(BaseNameField, MainAssembly);
            }
#endif
            Dictionary<String, ResourceSet> localResourceSets = _resourceSets;

            // If any calls to Close throw, at least leave ourselves in a
            // consistent state.
            _resourceSets = new Dictionary<String,ResourceSet>();
            _lastUsedResourceCache = new CultureNameResourceSetPair();
            
            lock(localResourceSets) {
                IDictionaryEnumerator setEnum = localResourceSets.GetEnumerator();

#if !FEATURE_CORECLR
                IDictionaryEnumerator setEnum2 = null;
#pragma warning disable 618
                if (ResourceSets != null) {
                    setEnum2 = ResourceSets.GetEnumerator();
                }
                ResourceSets = new Hashtable(); // for backwards compat
#pragma warning restore 618
#endif

                while (setEnum.MoveNext()) {
                    ((ResourceSet)setEnum.Value).Close();
                }

#if !FEATURE_CORECLR
                if (setEnum2 != null) {
                    while (setEnum2.MoveNext()) {
                        ((ResourceSet)setEnum2.Value).Close();
                    }
                }
#endif
            }
        }

        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public static ResourceManager CreateFileBasedResourceManager(String baseName, String resourceDir, Type usingResourceSet)
        {
            return new ResourceManager(baseName, resourceDir, usingResourceSet);
        }
      
        // Given a CultureInfo, GetResourceFileName generates the name for 
        // the binary file for the given CultureInfo.  This method uses 
        // CultureInfo's Name property as part of the file name for all cultures
        // other than the invariant culture.  This method does not touch the disk, 
        // and is used only to construct what a resource file name (suitable for
        // passing to the ResourceReader constructor) or a manifest resource file
        // name should look like.
        // 
        // This method can be overriden to look for a different extension,
        // such as ".ResX", or a completely different format for naming files.
        protected virtual String GetResourceFileName(CultureInfo culture) {
            StringBuilder sb = new StringBuilder(255);
            sb.Append(BaseNameField);
            // If this is the neutral culture, don't append culture name.
            if (!culture.HasInvariantCultureName)
            {
                CultureInfo.VerifyCultureName(culture.Name, true);
                sb.Append('.');
                sb.Append(culture.Name);
            }
            sb.Append(ResFileExtension);
            return sb.ToString();
        }

        // WARNING: This function must be kept in [....] with ResourceFallbackManager.GetEnumerator()
        // Return the first ResourceSet, based on the first culture ResourceFallbackManager would return
        internal ResourceSet GetFirstResourceSet(CultureInfo culture)
        {
            // Logic from ResourceFallbackManager.GetEnumerator()
            if (_neutralResourcesCulture != null && culture.Name == _neutralResourcesCulture.Name) 
            {
                culture = CultureInfo.InvariantCulture;
            }

            if(_lastUsedResourceCache != null) {
                lock (_lastUsedResourceCache) {
                    if (culture.Name == _lastUsedResourceCache.lastCultureName)
                        return _lastUsedResourceCache.lastResourceSet;
                }
            }

            // Look in the ResourceSet table
            Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
            ResourceSet rs = null;
            if (localResourceSets != null) 
            {
                lock (localResourceSets) 
                {
                    localResourceSets.TryGetValue(culture.Name, out rs);
                }
            }

            if (rs != null)
            {
                // update the cache with the most recent ResourceSet
                if (_lastUsedResourceCache != null) {
                    lock (_lastUsedResourceCache) {
                        _lastUsedResourceCache.lastCultureName = culture.Name;
                        _lastUsedResourceCache.lastResourceSet = rs;
                    }
                }
                return rs;
            }

            return null;
        }
        
        // Looks up a set of resources for a particular CultureInfo.  This is
        // not useful for most users of the ResourceManager - call 
        // GetString() or GetObject() instead.  
        //
        // The parameters let you control whether the ResourceSet is created 
        // if it hasn't yet been loaded and if parent CultureInfos should be 
        // loaded as well for resource inheritance.
        //         
        [System.Security.SecuritySafeCritical]  // auto-generated
        [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
        public virtual ResourceSet GetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) {
            if (null==culture)
                throw new ArgumentNullException("culture");
            Contract.EndContractBlock();

            Dictionary<String,ResourceSet> localResourceSets = _resourceSets;
            ResourceSet rs;
            if (localResourceSets != null) {
                lock (localResourceSets) {
                    if (localResourceSets.TryGetValue(culture.Name, out rs))
                        return rs;
                }
            }

            StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;

            if (UseManifest && culture.HasInvariantCultureName) {
                string fileName = GetResourceFileName(culture);
                RuntimeAssembly mainAssembly = (RuntimeAssembly)MainAssembly;
                Stream stream = mainAssembly.GetManifestResourceStream(_locationInfo, fileName, m_callingAssembly == MainAssembly, ref stackMark);
                if (createIfNotExists && stream!=null) {
                    rs = ((ManifestBasedResourceGroveler)resourceGroveler).CreateResourceSet(stream, MainAssembly);
                    AddResourceSet(localResourceSets, culture.Name, ref rs);                    
                    return rs;
                }
            }

            // Note: ideally we could plumb through the stack crawl mark here, but we must
            // call the virtual 3-argument InternalGetResourceSet method for compatibility.
            // Security-wise, we're not overly interested in protecting access to resources,
            // since full-trust callers can get them already and most resources are public.
            // Also, the JIT inliner could always inline a caller into another assembly's
            // method, so the entire idea of a security check written this way is ----.
            // So if we happen to return some resources in cases where we should really be
            // doing a demand for member access permissions, we're not overly concerned.
            // <
            return InternalGetResourceSet(culture, createIfNotExists, tryParents);
        }

        // InternalGetResourceSet is a non-threadsafe method where all the logic
        // for getting a resource set lives.  Access to it is controlled by
        // threadsafe methods such as GetResourceSet, GetString, & GetObject.  
        // This will take a minimal number of locks.
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
        protected virtual ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) 
        {
            Contract.Assert(culture != null, "culture != null");

            StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
            return InternalGetResourceSet(culture,createIfNotExists,tryParents, ref stackMark);
        }

        // InternalGetResourceSet is a non-threadsafe method where all the logic
        // for getting a resource set lives.  Access to it is controlled by
        // threadsafe methods such as GetResourceSet, GetString, & GetObject.  
        // This will take a minimal number of locks.
        [System.Security.SecurityCritical]
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private ResourceSet InternalGetResourceSet(CultureInfo requestedCulture, bool createIfNotExists, bool tryParents, ref StackCrawlMark stackMark)
        {
            Dictionary<String, ResourceSet> localResourceSets = _resourceSets;
            ResourceSet rs = null;
            CultureInfo foundCulture = null;
            lock (localResourceSets) {
                if (localResourceSets.TryGetValue(requestedCulture.Name, out rs)) {
#if !FEATURE_CORECLR && !MONO
                    if (FrameworkEventSource.IsInitialized) {
                        FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, requestedCulture.Name);
                    }
#endif
                    return rs;
                }
            }

            ResourceFallbackManager mgr = new ResourceFallbackManager(requestedCulture, _neutralResourcesCulture, tryParents);

            foreach (CultureInfo currentCultureInfo in mgr)
            {
#if !FEATURE_CORECLR && !MONO
                if (FrameworkEventSource.IsInitialized)
                {
                    FrameworkEventSource.Log.ResourceManagerLookingForResourceSet(BaseNameField, MainAssembly, currentCultureInfo.Name);
                }
#endif
                lock(localResourceSets) {
                    if (localResourceSets.TryGetValue(currentCultureInfo.Name, out rs)) {
#if !FEATURE_CORECLR && !MONO
                        if (FrameworkEventSource.IsInitialized)
                        {
                            FrameworkEventSource.Log.ResourceManagerFoundResourceSetInCache(BaseNameField, MainAssembly, currentCultureInfo.Name);
                        }
#endif
                        // we need to update the cache if we fellback
                        if(requestedCulture != currentCultureInfo) foundCulture = currentCultureInfo;
                        break;
                    }
                }

                // InternalGetResourceSet will never be threadsafe.  However, it must
                // be protected against reentrancy from the SAME THREAD.  (ie, calling
                // GetSatelliteAssembly may send some window messages or trigger the
                // Assembly load event, which could fail then call back into the 
                // ResourceManager).  It's happened.

                rs = resourceGroveler.GrovelForResourceSet(currentCultureInfo, localResourceSets, 
                                                           tryParents, createIfNotExists, ref stackMark);

                // found a ResourceSet; we're done
                if (rs != null)
                {
                    foundCulture = currentCultureInfo;
                    break;
                }

            }

            if (rs != null && foundCulture != null)
            {
                // add entries to the cache for the cultures we have gone through

                // currentCultureInfo now refers to the culture that had resources.
                // update cultures starting from requested culture up to the culture
                // that had resources.
                foreach (CultureInfo updateCultureInfo in mgr)
                {
                    AddResourceSet(localResourceSets, updateCultureInfo.Name, ref rs);

                    // stop when we've added current or reached invariant (top of chain)
                    if (updateCultureInfo == foundCulture)
                    {
                        break;
                    }
                } 
            }

            return rs;
        }

        // Simple helper to ease maintenance and improve readability.
        private static void AddResourceSet(Dictionary<String,ResourceSet> localResourceSets, String cultureName, ref ResourceSet rs)
        {
            // InternalGetResourceSet is both recursive and reentrant - 
            // assembly load callbacks in particular are a way we can call
            // back into the ResourceManager in unexpectedly on the same thread.
            lock(localResourceSets) {
                // If another thread added this culture, return that.
                ResourceSet lostRace;
                if (localResourceSets.TryGetValue(cultureName, out lostRace)) {
                    if (!Object.ReferenceEquals(lostRace, rs)) {
                        // Note: In certain cases, we can be trying to add a ResourceSet for multiple
                        // cultures on one thread, while a second thread added another ResourceSet for one
                        // of those cultures.  So when we lose the ----, we must make sure our ResourceSet 
                        // isn't in our dictionary before closing it.
                        if (!localResourceSets.ContainsValue(rs))
                            rs.Dispose();
                        rs = lostRace;
                    }
                }
                else {
                    localResourceSets.Add(cultureName, rs);
                }
            }
        }

        protected static Version GetSatelliteContractVersion(Assembly a)
        {
            // Ensure that the assembly reference is not null
            if (a == null) {
                throw new ArgumentNullException("a", Environment.GetResourceString("ArgumentNull_Assembly"));
            }
            Contract.EndContractBlock();

#if !FEATURE_WINDOWSPHONE
            String v = null;
            if (a.ReflectionOnly) {
                foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(a)) {
                    if (data.Constructor.DeclaringType == typeof(SatelliteContractVersionAttribute)) {
                        v = (String)data.ConstructorArguments[0].Value;
                        break;
                    }
                }

                if (v == null)
                    return null;
            }
            else {
                Object[] attrs = a.GetCustomAttributes(typeof(SatelliteContractVersionAttribute), false);
                if (attrs.Length == 0)
                    return null;
                Contract.Assert(attrs.Length == 1, "Cannot have multiple instances of SatelliteContractVersionAttribute on an assembly!");
                v = ((SatelliteContractVersionAttribute)attrs[0]).Version;
            }
            Version ver;
            try {
                ver = new Version(v);
            }
            catch(ArgumentOutOfRangeException e) {
                // Note we are prone to hitting infinite loops if mscorlib's
                // SatelliteContractVersionAttribute contains bogus values.
                // If this assert fires, please fix the build process for the
                // BCL directory.
                if (a == typeof(Object).Assembly) {
                    Contract.Assert(false, "mscorlib's SatelliteContractVersionAttribute is a malformed version string!");
                    return null;
                }

                throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSatelliteContract_Asm_Ver", a.ToString(), v), e);
            }
            return ver;
#else
            // On the phone return null. The calling code will use the assembly version instead to avoid potential type
            // and library loads caused by CA lookup. NetCF uses the assembly version always.
            return null;
#endif
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        protected static CultureInfo GetNeutralResourcesLanguage(Assembly a)
        {
            // This method should be obsolete - replace it with the one below.
            // Unfortunately, we made it protected.
            UltimateResourceFallbackLocation ignoringUsefulData = UltimateResourceFallbackLocation.MainAssembly;
            CultureInfo culture = ManifestBasedResourceGroveler.GetNeutralResourcesLanguage(a, ref ignoringUsefulData);
            return culture;
        }

        // IGNORES VERSION
        internal static bool CompareNames(String asmTypeName1,
                                          String typeName2, 
                                          AssemblyName asmName2)
        {
            Contract.Assert(asmTypeName1 != null, "asmTypeName1 was unexpectedly null");

            // First, compare type names
            int comma = asmTypeName1.IndexOf(',');
            if (((comma == -1) ? asmTypeName1.Length : comma) != typeName2.Length)
                return false;

            // case sensitive
            if (String.Compare(asmTypeName1, 0, typeName2, 0, typeName2.Length, StringComparison.Ordinal) != 0)
                return false;
            if (comma == -1)
                return true;

            // Now, compare assembly display names (IGNORES VERSION AND PROCESSORARCHITECTURE)
            // also, for  mscorlib ignores everything, since that's what the binder is going to do
            while(Char.IsWhiteSpace(asmTypeName1[++comma]));

            // case insensitive
            AssemblyName an1 = new AssemblyName(asmTypeName1.Substring(comma));
            if (String.Compare(an1.Name, asmName2.Name, StringComparison.OrdinalIgnoreCase) != 0)
                return false;

            // to match IsMscorlib() in VM
            if (String.Compare(an1.Name, "mscorlib", StringComparison.OrdinalIgnoreCase) == 0)
                return true;


            if ((an1.CultureInfo != null) && (asmName2.CultureInfo != null) &&
#if FEATURE_USE_LCID                
                (an1.CultureInfo.LCID != asmName2.CultureInfo.LCID)
#else
                (an1.CultureInfo.Name != asmName2.CultureInfo.Name)
#endif                
                )
                return false;

            byte[] pkt1 = an1.GetPublicKeyToken();
            byte[] pkt2 = asmName2.GetPublicKeyToken();
            if ((pkt1 != null) && (pkt2 != null)) {
                if (pkt1.Length != pkt2.Length)
                    return false;

                for(int i=0; i < pkt1.Length; i++) {
                    if(pkt1[i] != pkt2[i])
                        return false;
                }
            }

            return true;
        }

#if FEATURE_APPX
        [SecuritySafeCritical]
        // Throws WinRT hresults
        private string GetStringFromPRI(String stringName, String startingCulture, String neutralResourcesCulture) {
            Contract.Assert(_bUsingModernResourceManagement);
            Contract.Assert(_WinRTResourceManager != null);
            Contract.Assert(_PRIonAppXInitialized);
            Contract.Assert(AppDomain.IsAppXModel());
        
            if (stringName.Length == 0)
                return null;
            
            string resourceString = null;

            // Do not handle exceptions. See the comment in SetAppXConfiguration about throwing
            // exception types that the ResourceManager class is not documented to throw.
            resourceString = _WinRTResourceManager.GetString(
                                       stringName,
                                       String.IsNullOrEmpty(startingCulture) ? null : startingCulture,
                                       String.IsNullOrEmpty(neutralResourcesCulture) ? null : neutralResourcesCulture);
            
            return resourceString;
        }

        // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the type via reflection.
        // It would be better if we could just implement WindowsRuntimeResourceManager in mscorlib, but we can't, because
        // we can do very little with WinRT in mscorlib.
        [SecurityCritical]
        internal static WindowsRuntimeResourceManagerBase GetWinRTResourceManager()
        {
            Type WinRTResourceManagerType = Type.GetType("System.Resources.WindowsRuntimeResourceManager, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
            return (WindowsRuntimeResourceManagerBase)Activator.CreateInstance(WinRTResourceManagerType, true);
        }
#endif

        [NonSerialized]
        private bool _bUsingModernResourceManagement; // Written only by SetAppXConfiguration

#if FEATURE_APPX
        [NonSerialized]
        [SecurityCritical]
        private WindowsRuntimeResourceManagerBase _WinRTResourceManager; // Written only by SetAppXConfiguration

        [NonSerialized]
        private bool _PRIonAppXInitialized; // Written only by SetAppXConfiguration

        [NonSerialized]
        private PRIExceptionInfo _PRIExceptionInfo; // Written only by SetAppXConfiguration

        // When running under AppX, the following rules apply for resource lookup:
        //
        // Desktop
        // -------
        //
        // 1) For Framework assemblies, we always use satellite assembly based lookup.
        // 2) For non-FX assemblies, we use modern resource manager, with the premise being that app package
        //    contains the PRI resources since such assemblies are expected to be application assemblies.
        //
        // CoreCLR
        // -------
        //
        // 1) For Framework assemblies, we always use satellite assembly based lookup.
        // 2) For non-FX assemblies:
        //    
        //    a) If the assembly lives under PLATFORM_RESOURCE_ROOTS (as specified by the host during AppDomain creation),
        //       then we will use satellite assembly based lookup in assemblies like *.resources.dll.
        //   
        //    b) For any other non-FX assembly, we will use the modern resource manager with the premise that app package
        //       contains the PRI resources.
        [SecuritySafeCritical]
        private bool ShouldUseSatelliteAssemblyResourceLookupUnderAppX(RuntimeAssembly resourcesAssembly)
        {
            bool fUseSatelliteAssemblyResourceLookupUnderAppX = resourcesAssembly.IsFrameworkAssembly();
            
#if FEATURE_CORECLR     
            if (!fUseSatelliteAssemblyResourceLookupUnderAppX)
            {
                // Check to see if the assembly is under PLATFORM_RESOURCE_ROOTS. If it is, then we should use satellite assembly lookup for it.
                String platformResourceRoots = (String)(AppDomain.CurrentDomain.GetData("PLATFORM_RESOURCE_ROOTS"));
                if ((platformResourceRoots != null) && (platformResourceRoots != String.Empty))
                {
                    string resourceAssemblyPath = resourcesAssembly.Location;
                    
                    // Loop through the PLATFORM_RESOURCE_ROOTS and see if the assembly is contained in it.
                    foreach(string pathPlatformResourceRoot in platformResourceRoots.Split(Path.PathSeparator))
                    {
                        if (resourceAssemblyPath.StartsWith(pathPlatformResourceRoot, StringComparison.CurrentCultureIgnoreCase))
                        {
                            // Found the resource assembly to be present in one of the PLATFORM_RESOURCE_ROOT, so stop the enumeration loop.
                            fUseSatelliteAssemblyResourceLookupUnderAppX = true;
                            break;
                        }
                    }
                }
            }
#endif // FEATURE_CORECLR
            return fUseSatelliteAssemblyResourceLookupUnderAppX;
            
        }
        
        [SecuritySafeCritical]
#endif // FEATURE_APPX
        // Only call SetAppXConfiguration from ResourceManager constructors, and nowhere else.
        // Throws MissingManifestResourceException and WinRT HResults

        private void SetAppXConfiguration()
        {
            Contract.Assert(_bUsingModernResourceManagement == false); // Only this function writes to this member
#if FEATURE_APPX
            Contract.Assert(_WinRTResourceManager == null); // Only this function writes to this member
            Contract.Assert(_PRIonAppXInitialized == false); // Only this function writes to this member
            Contract.Assert(_PRIExceptionInfo == null); // Only this function writes to this member

            bool bUsingSatelliteAssembliesUnderAppX = false;

            RuntimeAssembly resourcesAssembly = (RuntimeAssembly)MainAssembly;

            if (resourcesAssembly == null)
                resourcesAssembly = m_callingAssembly;

            if (resourcesAssembly != null)
            {
                if (resourcesAssembly != typeof(Object).Assembly) // We are not loading resources for mscorlib
                {
                    // Cannot load the WindowsRuntimeResourceManager when in a compilation process, since it
                    // lives in System.Runtime.WindowsRuntime and only mscorlib may be loaded for execution.
                    if (AppDomain.IsAppXModel() && !AppDomain.IsAppXNGen)
                    {
                        s_IsAppXModel = true;

                        // If we have the type information from the ResourceManager(Type) constructor, we use it. Otherwise, we use BaseNameField.
                        String reswFilename = _locationInfo == null ? BaseNameField : _locationInfo.FullName;

                        // The only way this can happen is if a class inherited from ResourceManager and
                        // did not set the BaseNameField before calling the protected ResourceManager() constructor.
                        // For other constructors, we would already have thrown an ArgumentNullException by now.
                        // Throwing an ArgumentNullException now is not the right thing to do because technically
                        // ResourceManager() takes no arguments, and because it is not documented as throwing
                        // any exceptions. Instead, let's go through the rest of the initialization with this set to
                        // an empty string. We may in fact fail earlier for another reason, but otherwise we will
                        // throw a MissingManifestResourceException when GetString is called indicating that a
                        // resW filename called "" could not be found.
                        if (reswFilename == null)
                            reswFilename = String.Empty;

                        WindowsRuntimeResourceManagerBase WRRM = null;
                        bool bWRRM_Initialized = false;
                        
                        if (AppDomain.IsAppXDesignMode())
                        {
                            WRRM = GetWinRTResourceManager();
                            try {
                                PRIExceptionInfo exceptionInfo; // If the exception info is filled in, we will ignore it.
                                bWRRM_Initialized = WRRM.Initialize(resourcesAssembly.Location, reswFilename, out exceptionInfo);
                                bUsingSatelliteAssembliesUnderAppX = !bWRRM_Initialized;
                            }
                            catch(Exception e)
                            {
                                bUsingSatelliteAssembliesUnderAppX = true;
                                if (e.IsTransient)
                                    throw;
                            }
                        }

                        if (!bUsingSatelliteAssembliesUnderAppX)
                        {
                            // See AssemblyNative::IsFrameworkAssembly for details on which kinds of assemblies are considered Framework assemblies.
                            // The Modern Resource Manager is not used for such assemblies - they continue to use satellite assemblies (i.e. .resources.dll files).
                            _bUsingModernResourceManagement = !ShouldUseSatelliteAssemblyResourceLookupUnderAppX(resourcesAssembly); 

                            if (_bUsingModernResourceManagement)
                            {
                                // Only now are we certain that we need the PRI file.

                                // Note that if IsAppXDesignMode is false, we haven't checked if the PRI file exists.
                                // This is by design. We will find out in the call to WindowsRuntimeResourceManager.Initialize below.

                                // At this point it is important NOT to set _bUsingModernResourceManagement to false
                                // if the PRI file does not exist because we are now certain we need to load PRI
                                // resources. We want to fail by throwing a MissingManifestResourceException
                                // if WindowsRuntimeResourceManager.Initialize fails to locate the PRI file. We do not
                                // want to fall back to using satellite assemblies anymore. Note that we would not throw
                                // the MissingManifestResourceException from this function, but from GetString. See the
                                // comment below on the reason for this.

                                if (WRRM != null && bWRRM_Initialized)
                                {
                                    // Reuse the one successfully created earlier
                                    _WinRTResourceManager = WRRM;
                                    _PRIonAppXInitialized = true;
                                }
                                else 
                                {
                                    _WinRTResourceManager = GetWinRTResourceManager();
                                    
                                    try {
                                        _PRIonAppXInitialized = _WinRTResourceManager.Initialize(resourcesAssembly.Location, reswFilename, out _PRIExceptionInfo);

                                        // Note that _PRIExceptionInfo might be null - this is OK.
                                        // In that case we will just throw the generic
                                        // MissingManifestResource_NoPRIresources exception.
                                        // See the implementation of GetString for more details.
                                    }
                                    // We would like to be able to throw a MissingManifestResourceException here if PRI resources
                                    // could not be loaded for a recognized reason. However, the ResourceManager constructors
                                    // that call SetAppXConfiguration are not documented as throwing MissingManifestResourceException,
                                    // and since they are part of the portable profile, we cannot start throwing a new exception type
                                    // as that would break existing portable libraries. Hence we must save the exception information
                                    // now and throw the exception on the first call to GetString.
                                    catch(FileNotFoundException)
                                    {
                                        // We will throw MissingManifestResource_NoPRIresources from GetString
                                        // when we see that _PRIonAppXInitialized is false.
                                    }
                                    catch(Exception e)
                                    {
                                        // ERROR_MRM_MAP_NOT_FOUND can be thrown by the call to ResourceManager.get_AllResourceMaps
                                        // in WindowsRuntimeResourceManager.Initialize.
                                        // In this case _PRIExceptionInfo is now null and we will just throw the generic
                                        // MissingManifestResource_NoPRIresources exception.
                                        // See the implementation of GetString for more details.
                                        if (e.HResult != __HResults.ERROR_MRM_MAP_NOT_FOUND)
                                            throw; // Unexpected exception code. Bubble it up to the caller.
                                    }
                                    // Allow all other exception types to bubble up to the caller.

                                    // Yes, this causes us to potentially throw exception types that are not documented.

                                    // Ultimately the tradeoff is the following:
                                    // -We could ignore unknown exceptions or rethrow them as inner exceptions
                                    // of exceptions that the ResourceManager class is already documented as throwing.
                                    // This would allow existing portable libraries to gracefully recover if they don't care
                                    // too much about the ResourceManager object they are using. However it could
                                    // mask potentially fatal errors that we are not aware of, such as a disk drive failing.


                                    // The alternative, which we chose, is to throw unknown exceptions. This may tear
                                    // down the process if the portable library and app don't expect this exception type.
                                    // On the other hand, this won't mask potentially fatal errors we don't know about.
                                }
                            }
                        }
                    }
                }
            }
            // resourcesAssembly == null should not happen but it can. See the comment on Assembly.GetCallingAssembly.
            // However for the sake of 100% backwards compatibility on Win7 and below, we must leave
            // _bUsingModernResourceManagement as false.
#endif // FEATURE_APPX            
        }

        // Looks up a resource value for a particular name.  Looks in the 
        // current thread's CultureInfo, and if not found, all parent CultureInfos.
        // Returns null if the resource wasn't found.
        // 
        public virtual String GetString(String name) {
            return GetString(name, (CultureInfo)null);
        }
        
        // Looks up a resource value for a particular name.  Looks in the 
        // specified CultureInfo, and if not found, all parent CultureInfos.
        // Returns null if the resource wasn't found.
        // 
        public virtual String GetString(String name, CultureInfo culture) {
            if (null==name)
                throw new ArgumentNullException("name");
            Contract.EndContractBlock();

#if FEATURE_APPX
            if(s_IsAppXModel)
            {
                 // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
                 // null it out, so that we re-compute it.  If we use modern resource lookup, we may end up getting a "better"
                 // match, since CultureInfo objects can't represent all the different languages the AppX resource model supports.
                 // For classic resources, this causes us to ignore the languages list and instead use the older Win32 behavior,
                 // which is the design choice we've made. (See the call a little later to GetCurrentUICultureNoAppX()).
                 if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
                 {
                     culture = null;
                 }              
            }

            if (_bUsingModernResourceManagement)
            {
                if (_PRIonAppXInitialized == false)
                {
                    // Always throw if we did not fully succeed in initializing the WinRT Resource Manager.

                    if (_PRIExceptionInfo != null && _PRIExceptionInfo._PackageSimpleName != null && _PRIExceptionInfo._ResWFile != null)
                        throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_ResWFileNotLoaded", _PRIExceptionInfo._ResWFile, _PRIExceptionInfo._PackageSimpleName));

                    throw new MissingManifestResourceException(Environment.GetResourceString("MissingManifestResource_NoPRIresources"));
                }
            
                // Throws WinRT hresults.
                return GetStringFromPRI(name,
                                        culture == null ? null : culture.Name,
                                        _neutralResourcesCulture.Name);
            }
            else
#endif // FEATURE_APPX
            {
                if (null==culture) {
                    // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
                    // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
                    culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
                }
    
#if !FEATURE_CORECLR && !MONO
                if (FrameworkEventSource.IsInitialized)
                {
                    FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
                }
#endif
                ResourceSet last = GetFirstResourceSet(culture);

                if (last != null)
                {
                    String value = last.GetString(name, _ignoreCase);
                    if (value != null)
                        return value;
                }
                
                
                // This is the CultureInfo hierarchy traversal code for resource 
                // lookups, similar but necessarily orthogonal to the ResourceSet 
                // lookup logic.
                ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
                foreach (CultureInfo currentCultureInfo in mgr) {

                    ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
                    if (rs == null)
                        break;

                    if (rs != last) {
                        String value = rs.GetString(name, _ignoreCase);
                        if (value != null)
                        {
                            // update last used ResourceSet
                            if (_lastUsedResourceCache != null) {
                                lock (_lastUsedResourceCache) {
                                    _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
                                    _lastUsedResourceCache.lastResourceSet = rs;
                                }
                            }
                            return value;
                        }

                        last = rs;
                    }
                }

#if !FEATURE_CORECLR && !MONO
                if (FrameworkEventSource.IsInitialized)
                {
                    FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
                }
#endif
            }

            return null;
        }
        
        
        // Looks up a resource value for a particular name.  Looks in the 
        // current thread's CultureInfo, and if not found, all parent CultureInfos.
        // Returns null if the resource wasn't found.
        // 
        public virtual Object GetObject(String name) {
            return GetObject(name, (CultureInfo)null, true);
        }
        
        // Looks up a resource value for a particular name.  Looks in the 
        // specified CultureInfo, and if not found, all parent CultureInfos.
        // Returns null if the resource wasn't found.
        public virtual Object GetObject(String name, CultureInfo culture) {
            return GetObject(name, culture, true);
        }

        private Object GetObject(String name, CultureInfo culture, bool wrapUnmanagedMemStream)
        {
            if (null==name)
                throw new ArgumentNullException("name");
            Contract.EndContractBlock();

#if FEATURE_APPX
            if(s_IsAppXModel)
            {
                 // If the caller explictily passed in a culture that was obtained by calling CultureInfo.CurrentUICulture,
                 // null it out, so that we re-compute it based on the Win32 value and not the AppX language list value.
                 // (See the call a little later to GetCurrentUICultureNoAppX()).
                 if(Object.ReferenceEquals(culture, CultureInfo.CurrentUICulture))
                 {
                     culture = null;
                 }              
            }
#endif

            if (null==culture) {
                // When running inside AppX we want to ignore the languages list when trying to come up with our CurrentUICulture.
                // This line behaves the same way as CultureInfo.CurrentUICulture would have in .NET 4
                culture = Thread.CurrentThread.GetCurrentUICultureNoAppX();
            }

#if !FEATURE_CORECLR && !MONO
            if (FrameworkEventSource.IsInitialized)
            {
                FrameworkEventSource.Log.ResourceManagerLookupStarted(BaseNameField, MainAssembly, culture.Name);
            }
#endif
            ResourceSet last = GetFirstResourceSet(culture);
            if (last != null)
            {
                Object value = last.GetObject(name, _ignoreCase);

                if (value != null) 
                {
                    UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
                    if (stream != null && wrapUnmanagedMemStream)
                        return new UnmanagedMemoryStreamWrapper(stream);
                    else
                        return value;
                }
            }
            
            // This is the CultureInfo hierarchy traversal code for resource 
            // lookups, similar but necessarily orthogonal to the ResourceSet 
            // lookup logic.
            ResourceFallbackManager mgr = new ResourceFallbackManager(culture, _neutralResourcesCulture, true);
            
            foreach (CultureInfo currentCultureInfo in mgr) {
                // Note: Technically this method should be passed in a stack crawl mark that we then pass
                // to InternalGetResourceSet for ensuring we demand permissions to read your private resources
                // if you're reading resources from an assembly other than yourself.  But, we must call our
                // three argument overload (without the stack crawl mark) for compatibility.  After 
                // consideration, we aren't worried about the security impact.
                ResourceSet rs = InternalGetResourceSet(currentCultureInfo, true, true);
                if (rs == null)
                    break;

                if (rs != last) {
                    Object value = rs.GetObject(name, _ignoreCase);
                    if (value != null) {
                        // update the last used ResourceSet
                        if (_lastUsedResourceCache != null) {
                            lock (_lastUsedResourceCache) {
                                _lastUsedResourceCache.lastCultureName = currentCultureInfo.Name;
                                _lastUsedResourceCache.lastResourceSet = rs;
                            }
                        }

                        UnmanagedMemoryStream stream = value as UnmanagedMemoryStream;
                        if (stream != null && wrapUnmanagedMemStream)
                            return new UnmanagedMemoryStreamWrapper(stream);
                        else
                            return value;
                    }

                    last = rs;
                }
            }

#if !FEATURE_CORECLR && !MONO
            if (FrameworkEventSource.IsInitialized)
            {
                FrameworkEventSource.Log.ResourceManagerLookupFailed(BaseNameField, MainAssembly, culture.Name);
            }
#endif
            return null;
        }

        [ComVisible(false)]
        public UnmanagedMemoryStream GetStream(String name) {
            return GetStream(name, (CultureInfo)null);
        }
        
        [ComVisible(false)]
        public UnmanagedMemoryStream GetStream(String name, CultureInfo culture) {
            Object obj = GetObject(name, culture, false);
            UnmanagedMemoryStream ums = obj as UnmanagedMemoryStream;
            if (ums == null && obj != null)
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotStream_Name", name));
            return ums;
        }

#if RESOURCE_SATELLITE_CONFIG
        // Internal helper method - gives an end user the ability to prevent
        // satellite assembly probes for certain cultures via a config file.
        [System.Security.SecurityCritical]  // auto-generated
        private bool TryLookingForSatellite(CultureInfo lookForCulture)
        {
            if (!_checkedConfigFile) {
                lock (this) {
                    if (!_checkedConfigFile) {
                        _checkedConfigFile = true;
                        _installedSatelliteInfo = GetSatelliteAssembliesFromConfig();
                    }
                }
            }

            if (_installedSatelliteInfo == null)
                return true;

            String[] installedSatellites = (String[]) _installedSatelliteInfo[MainAssembly.FullName];

            if (installedSatellites == null)
                return true;

            // The config file told us what satellites might be installed.
            int pos = Array.IndexOf(installedSatellites, lookForCulture.Name);

#if !FEATURE_CORECLR && !MONO
            if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled()) {
                if (pos < 0) {
                    FrameworkEventSource.Log.ResourceManagerCultureNotFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
                }
                else {
                    FrameworkEventSource.Log.ResourceManagerCultureFoundInConfigFile(BaseNameField, MainAssembly, lookForCulture.Name);
                }
            }
#endif
            return pos >= 0;
        }

        // Note: There is one config file per appdomain.  This is not 
        // per-process nor per-assembly.
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private Hashtable GetSatelliteAssembliesFromConfig()
        {
#if FEATURE_FUSION

            String fileName = AppDomain.CurrentDomain.FusionStore.ConfigurationFileInternal;
            if (fileName == null) {
                return null;
            }

            // Don't do a security assert.  We need to support semi-trusted 
            // scenarios, but asserting here causes infinite resource lookups
            // while initializing security & looking up mscorlib's config file.
            // Use internal methods to bypass security checks.

            // If we're dealing with a local file name or a UNC path instead 
            // of a URL, check to see if the file exists here for perf (avoids
            // throwing a FileNotFoundException).
            if (fileName.Length >= 2 && 
                ((fileName[1] == Path.VolumeSeparatorChar) || (fileName[0] == Path.DirectorySeparatorChar && fileName[1] == Path.DirectorySeparatorChar)) &&
                !File.InternalExists(fileName))
                return null;

            ConfigTreeParser parser = new ConfigTreeParser();
            String queryPath = "/configuration/satelliteassemblies";
            ConfigNode node = null;
            // Catch exceptions in case a web app doesn't have a config file.
            try {
                node = parser.Parse(fileName, queryPath, true);
            }
            catch(Exception) {}

            if (node == null) {
                return null;
            }

            // The application config file will contain sections like this:
            // <?xml version="1.0"?>
            // <configuration>
            //     <satelliteassemblies>
            //         <assembly name="mscorlib, Version=..., PublicKeyToken=...">
            //             <culture>fr</culture>
            //         </assembly>
            //         <assembly name="UserAssembly, ...">
            //             <culture>fr-FR</culture>
            //             <culture>de-CH</culture>
            //         </assembly>
            //         <assembly name="UserAssembly2, ...">
            //         </assembly>
            //    </satelliteassemblies>
            // </configuration>
            Hashtable satelliteInfo = new Hashtable(StringComparer.OrdinalIgnoreCase);
            foreach(ConfigNode assemblyNode in node.Children) {
                if (!String.Equals(assemblyNode.Name, "assembly"))
                    throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTag", Path.GetFileName(fileName), assemblyNode.Name));

                if (assemblyNode.Attributes.Count == 0)
                    throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagNoAttr", Path.GetFileName(fileName)));

                DictionaryEntry de = (DictionaryEntry) assemblyNode.Attributes[0];
                String assemblyName = (String) de.Value;
                if (!String.Equals(de.Key, "name") || String.IsNullOrEmpty(assemblyName) || assemblyNode.Attributes.Count > 1) 
                    throw new ApplicationException(Environment.GetResourceString("XMLSyntax_InvalidSyntaxSatAssemTagBadAttr", Path.GetFileName(fileName), de.Key, de.Value));

                ArrayList list = new ArrayList(5);
                foreach(ConfigNode child in assemblyNode.Children)
                    if (child.Value != null)
                        list.Add(child.Value);

                String[] satellites = new String[list.Count];
                for(int i=0; i<satellites.Length; i++) {
                    String cultureName = (String)list[i];
                    satellites[i] = cultureName;
#if !FEATURE_CORECLR && !MONO
                    if (FrameworkEventSource.IsInitialized)
                    {
                        FrameworkEventSource.Log.ResourceManagerAddingCultureFromConfigFile(BaseNameField, MainAssembly, cultureName);
                    }
#endif
                }

                satelliteInfo.Add(assemblyName, satellites);
            }

            return satelliteInfo;
#else
            return null;
#endif //FEATURE_FUSION

        }
#endif  // RESOURCE_SATELLITE_CONFIG

        internal class ResourceManagerMediator
        {
            private ResourceManager _rm;

            internal ResourceManagerMediator(ResourceManager rm)
            {
                if (rm == null)
                {
                    throw new ArgumentNullException("rm");
                }
                _rm = rm;
            }

            // NEEDED ONLY BY FILE-BASED
            internal String ModuleDir
            {
                get { return _rm.moduleDir; }
            }

            // NEEDED BOTH BY FILE-BASED  AND ----Y-BASED
            internal Type LocationInfo
            {
                get { return _rm._locationInfo; }
            }

            internal Type UserResourceSet 
            {
                get { return _rm._userResourceSet; }
            }

            internal String BaseNameField
            {
                get { return _rm.BaseNameField; }
            }

            internal CultureInfo NeutralResourcesCulture
            {
                get { return _rm._neutralResourcesCulture; }
                set { _rm._neutralResourcesCulture = value; }
            }

            internal String GetResourceFileName(CultureInfo culture)
            {
                return _rm.GetResourceFileName(culture);
            }

            // NEEDED ONLY BY ----Y-BASED
            internal bool LookedForSatelliteContractVersion
            {
                get { return _rm._lookedForSatelliteContractVersion; }
                set { _rm._lookedForSatelliteContractVersion = value; }
            }

            internal Version SatelliteContractVersion
            {
                get { return _rm._satelliteContractVersion; }
                set { _rm._satelliteContractVersion = value; }
            }

            internal Version ObtainSatelliteContractVersion(Assembly a)
            {
                return ResourceManager.GetSatelliteContractVersion(a);
            }

            internal UltimateResourceFallbackLocation FallbackLoc
            {
                get { return _rm.FallbackLocation; } 
                set { _rm._fallbackLoc = value; }
            }

            internal RuntimeAssembly CallingAssembly
            {
                get { return _rm.m_callingAssembly; }
            }

            internal RuntimeAssembly MainAssembly
            {
                get { return (RuntimeAssembly)_rm.MainAssembly; }
            }

            // this is weird because we have BaseNameField accessor above, but we're sticking
            // with it for compat.
            internal String BaseName
            {
                get { return _rm.BaseName; }
            }


#if RESOURCE_SATELLITE_CONFIG
            [System.Security.SecurityCritical]  // auto-generated
            internal bool TryLookingForSatellite(CultureInfo lookForCulture)
            {
                return _rm.TryLookingForSatellite(lookForCulture);
            }
#endif

        }
    }
}
