We need you! We're working hard on the next version of Developer Fusion - Let us know what you think we should be up to!

The Labs \ Source Viewer \ SSCLI \ System.Resources \ RuntimeResourceSet

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  RuntimeResourceSet
  18. **
  19. **
  20. ** Purpose: CultureInfo-specific collection of resources.
  21. **
  22. **
  23. ===========================================================*/
  24. namespace System.Resources
  25. {
  26.     using System;
  27.     using System.IO;
  28.     using System.Collections;
  29.     using System.Collections.Generic;
  30.     using System.Globalization;
  31.     using System.Reflection;
  32.     using System.Runtime.Versioning;
  33.    
  34.     //
  35.     //
  36.     //
  37.     //
  38.     //
  39.     //
  40.     //
  41.     //
  42.     //
  43.     //
  44.     //
  45.     //
  46.     //
  47.     //
  48.     //
  49.     //
  50.     //
  51.     //
  52.     //
  53.     //
  54.     //
  55.     internal sealed class RuntimeResourceSet : ResourceSet, IEnumerable
  56.     {
  57.         internal const int Version = 2;
  58.         // File format version number
  59.         // Cache for resources. Key is the resource name, which can be cached
  60.         // for arbitrarily long times, since the object is usually a string
  61.         // literal that will live for the lifetime of the appdomain. The
  62.         // value is a ResourceLocator instance, which might cache the object.
  63.         private Dictionary<string, ResourceLocator> _resCache;
  64.        
  65.        
  66.         // For our special load-on-demand reader, cache the cast. The
  67.         // RuntimeResourceSet's implementation knows how to treat this reader specially.
  68.         private ResourceReader _defaultReader;
  69.        
  70.         private Dictionary<string, ResourceLocator> _caseInsensitiveTable;
  71.        
  72.         // If we're not using our custom reader, then enumerate through all
  73.         // the resources once, adding them into the table.
  74.         private bool _haveReadFromReader;
  75.        
  76.         [ResourceExposure(ResourceScope.Machine)]
  77.         [ResourceConsumption(ResourceScope.Machine)]
  78.         internal RuntimeResourceSet(string fileName) : base(false)
  79.         {
  80.             BCLDebug.Log("RESMGRFILEFORMAT", "RuntimeResourceSet .ctor(String)");
  81.             _resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
  82.             Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
  83.             _defaultReader = new ResourceReader(stream, _resCache);
  84.             Reader = _defaultReader;
  85.         }
  86.        
  87.         internal RuntimeResourceSet(Stream stream) : base(false)
  88.         {
  89.             BCLDebug.Log("RESMGRFILEFORMAT", "RuntimeResourceSet .ctor(Stream)");
  90.             _resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
  91.             _defaultReader = new ResourceReader(stream, _resCache);
  92.             Reader = _defaultReader;
  93.         }
  94.        
  95.         protected override void Dispose(bool disposing)
  96.         {
  97.             if (Reader == null)
  98.                 return;
  99.            
  100.             if (disposing) {
  101.                 lock (Reader) {
  102.                     _resCache = null;
  103.                     if (_defaultReader != null) {
  104.                         _defaultReader.Close();
  105.                         _defaultReader = null;
  106.                     }
  107.                     _caseInsensitiveTable = null;
  108.                     // Set Reader to null to avoid a race in GetObject.
  109.                     base.Dispose(disposing);
  110.                 }
  111.             }
  112.             else {
  113.                 // Just to make sure we always clear these fields in the future...
  114.                 _resCache = null;
  115.                 _caseInsensitiveTable = null;
  116.                 _defaultReader = null;
  117.                 base.Dispose(disposing);
  118.             }
  119.         }
  120.        
  121.         public override IDictionaryEnumerator GetEnumerator()
  122.         {
  123.             return GetEnumeratorHelper();
  124.         }
  125.        
  126.         IEnumerator IEnumerable.GetEnumerator()
  127.         {
  128.             return GetEnumeratorHelper();
  129.         }
  130.        
  131.         private IDictionaryEnumerator GetEnumeratorHelper()
  132.         {
  133.             IResourceReader copyOfReader = Reader;
  134.             if (copyOfReader == null || _resCache == null)
  135.                 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
  136.            
  137.             return copyOfReader.GetEnumerator();
  138.         }
  139.        
  140.        
  141.         public override string GetString(string key)
  142.         {
  143.             object o = GetObject(key, false, true);
  144.             return (string)o;
  145.         }
  146.        
  147.         public override string GetString(string key, bool ignoreCase)
  148.         {
  149.             object o = GetObject(key, ignoreCase, true);
  150.             return (string)o;
  151.         }
  152.        
  153.         public override object GetObject(string key)
  154.         {
  155.             return GetObject(key, false, false);
  156.         }
  157.        
  158.         public override object GetObject(string key, bool ignoreCase)
  159.         {
  160.             return GetObject(key, ignoreCase, false);
  161.         }
  162.        
  163.         private object GetObject(string key, bool ignoreCase, bool isString)
  164.         {
  165.             if (key == null)
  166.                 throw new ArgumentNullException("key");
  167.             Dictionary<string, ResourceLocator> copyOfCache = _resCache;
  168.             if (Reader == null || copyOfCache == null)
  169.                 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
  170.            
  171.             object value = null;
  172.             ResourceLocator resLocation;
  173.             if (copyOfCache.TryGetValue(key, out resLocation)) {
  174.                 value = ResolveResourceLocator(resLocation, key, copyOfCache, false);
  175.                 return value;
  176.             }
  177.            
  178.             lock (Reader) {
  179.                 if (Reader == null)
  180.                     throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_ResourceSet"));
  181.                
  182.                 if (_defaultReader != null) {
  183.                     BCLDebug.Log("RESMGRFILEFORMAT", "Going down fast path in RuntimeResourceSet::GetObject");
  184.                    
  185.                     // Find the offset within the data section
  186.                     int dataPos = -1;
  187.                     if (_resCache.TryGetValue(key, out resLocation)) {
  188.                         value = resLocation.Value;
  189.                         dataPos = resLocation.DataPosition;
  190.                     }
  191.                    
  192.                     if (dataPos == -1 && value == null) {
  193.                         dataPos = _defaultReader.FindPosForResource(key);
  194.                     }
  195.                    
  196.                     if (dataPos != -1 && value == null) {
  197.                         BCLDebug.Assert(dataPos >= 0, "data section offset cannot be negative!");
  198.                         // Normally calling LoadString or LoadObject requires
  199.                         // taking a lock. Note that in this case, we took a
  200.                         // lock on the entire RuntimeResourceSet, which is
  201.                         // sufficient since we never pass this ResourceReader
  202.                         // to anyone else.
  203.                         ResourceTypeCode typeCode;
  204.                         if (isString) {
  205.                             value = _defaultReader.LoadString(dataPos);
  206.                             typeCode = ResourceTypeCode.String;
  207.                         }
  208.                         else {
  209.                             value = _defaultReader.LoadObject(dataPos, out typeCode);
  210.                         }
  211.                        
  212.                         resLocation = new ResourceLocator(dataPos, (ResourceLocator.CanCache(typeCode)) ? value : null);
  213.                         lock (_resCache) {
  214.                             _resCache[key] = resLocation;
  215.                         }
  216.                     }
  217.                    
  218.                     if (value != null || !ignoreCase) {
  219.                        
  220.                         return value;
  221.                         // may be null
  222.                     }
  223.                 }
  224.                 // if (_defaultReader != null)
  225.                 // At this point, we either don't have our default resource reader
  226.                 // or we haven't found the particular resource we're looking for
  227.                 // and may have to search for it in a case-insensitive way.
  228.                 if (!_haveReadFromReader) {
  229.                     // If necessary, init our case insensitive hash table.
  230.                     if (ignoreCase && _caseInsensitiveTable == null) {
  231.                         _caseInsensitiveTable = new Dictionary<string, ResourceLocator>(StringComparer.OrdinalIgnoreCase);
  232.                     }
  233.                     #if _DEBUG
  234.                     BCLDebug.Perf(!ignoreCase, "Using case-insensitive lookups is bad perf-wise. Consider capitalizing " + key + " correctly in your source");
  235.                     #endif
  236.                    
  237.                     if (_defaultReader == null) {
  238.                         IDictionaryEnumerator en = Reader.GetEnumerator();
  239.                         while (en.MoveNext()) {
  240.                             DictionaryEntry entry = en.Entry;
  241.                             string readKey = (string)entry.Key;
  242.                             ResourceLocator resLoc = new ResourceLocator(-1, entry.Value);
  243.                             _resCache.Add(readKey, resLoc);
  244.                             if (ignoreCase)
  245.                                 _caseInsensitiveTable.Add(readKey, resLoc);
  246.                         }
  247.                         // Only close the reader if it is NOT our default one,
  248.                         // since we need it around to resolve ResourceLocators.
  249.                         if (!ignoreCase)
  250.                             Reader.Close();
  251.                     }
  252.                     else {
  253.                         BCLDebug.Assert(ignoreCase, "This should only happen for case-insensitive lookups");
  254.                         ResourceReader.ResourceEnumerator en = _defaultReader.GetEnumeratorInternal();
  255.                         while (en.MoveNext()) {
  256.                             // Note: Always ask for the resource key before the data position.
  257.                             string currentKey = (string)en.Key;
  258.                             int dataPos = en.DataPosition;
  259.                             ResourceLocator resLoc = new ResourceLocator(dataPos, null);
  260.                             _caseInsensitiveTable.Add(currentKey, resLoc);
  261.                         }
  262.                     }
  263.                     _haveReadFromReader = true;
  264.                 }
  265.                 object obj = null;
  266.                 bool found = false;
  267.                 bool keyInWrongCase = false;
  268.                 if (_defaultReader != null) {
  269.                     if (_resCache.TryGetValue(key, out resLocation))
  270.                         found = true;
  271.                 }
  272.                 if (!found && ignoreCase) {
  273.                     if (_caseInsensitiveTable.TryGetValue(key, out resLocation)) {
  274.                         found = true;
  275.                         keyInWrongCase = true;
  276.                     }
  277.                 }
  278.                 if (found) {
  279.                     obj = ResolveResourceLocator(resLocation, key, copyOfCache, keyInWrongCase);
  280.                 }
  281.                 return obj;
  282.             }
  283.             // lock(Reader)
  284.         }
  285.        
  286.         // The last parameter indicates whether the lookup required a
  287.         // case-insensitive lookup to succeed, indicating we shouldn't add
  288.         // the ResourceLocation to our case-sensitive cache.
  289.         private object ResolveResourceLocator(ResourceLocator resLocation, string key, Dictionary<string, ResourceLocator> copyOfCache, bool keyInWrongCase)
  290.         {
  291.             // We need to explicitly resolve loosely linked manifest
  292.             // resources, and we need to resolve ResourceLocators with null objects.
  293.             object value = resLocation.Value;
  294.             if (value == null) {
  295.                 ResourceTypeCode typeCode;
  296.                 lock (Reader) {
  297.                     value = _defaultReader.LoadObject(resLocation.DataPosition, out typeCode);
  298.                 }
  299.                 if (!keyInWrongCase && ResourceLocator.CanCache(typeCode)) {
  300.                     resLocation.Value = value;
  301.                     copyOfCache[key] = resLocation;
  302.                 }
  303.             }
  304.             return value;
  305.         }
  306.     }
  307. }

Developer Fusion