Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / resources / resourcefallbackmanager.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  ResourceFallbackManager
9 ** 
10 ** <OWNER>Microsoft</OWNER>
11 **
12 **
13 ** Purpose: Encapsulates CultureInfo fallback for resource 
14 ** lookup
15 **
16 ** 
17 ===========================================================*/
18
19 using System;
20 using System.Collections;
21 using System.Collections.Generic;
22 #if FEATURE_CORECLR
23 using System.Diagnostics.Contracts;
24 #endif
25 using System.Globalization;
26 using System.Runtime.CompilerServices;
27 using System.Runtime.InteropServices;
28 using System.Runtime.Versioning;
29
30 namespace System.Resources
31 {
32     internal class ResourceFallbackManager : IEnumerable<CultureInfo>
33     {
34         private CultureInfo m_startingCulture;
35         private CultureInfo m_neutralResourcesCulture;
36         private bool m_useParents;
37
38 // Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
39 #if FEATURE_CORECLR
40         // This is a cache of the thread, process, user, and OS-preferred fallback cultures.
41         // However, each thread may have a different value, and these may change during the
42         // lifetime of the process.  So this cache must be verified each time we use it.
43         // Hence, we'll keep an array of strings for culture names & check it each time,
44         // but we'll really cache an array of CultureInfo's.  Using thread-local statics
45         // as well to avoid differences across threads.
46         [ThreadStatic]
47         private static CultureInfo[] cachedOsFallbackArray;
48 #endif // FEATURE_CORECLR
49
50         internal ResourceFallbackManager(CultureInfo startingCulture, CultureInfo neutralResourcesCulture, bool useParents)
51         {
52             if (startingCulture != null)
53             {
54                 m_startingCulture = startingCulture;
55             }
56             else
57             {
58                 m_startingCulture = CultureInfo.CurrentUICulture;
59             }
60
61             m_neutralResourcesCulture = neutralResourcesCulture;
62             m_useParents = useParents;
63         }
64
65         IEnumerator IEnumerable.GetEnumerator()
66         {
67             return GetEnumerator();
68         }
69
70         // WARING: This function must be kept in sync with ResourceManager.GetFirstResourceSet()
71         public IEnumerator<CultureInfo> GetEnumerator()
72         {
73             bool reachedNeutralResourcesCulture = false;
74
75             // 1. starting culture chain, up to neutral
76             CultureInfo currentCulture = m_startingCulture;
77             do
78             {
79                 if (m_neutralResourcesCulture != null && currentCulture.Name == m_neutralResourcesCulture.Name) 
80                 {
81                     // Return the invariant culture all the time, even if the UltimateResourceFallbackLocation
82                     // is a satellite assembly.  This is fixed up later in ManifestBasedResourceGroveler::UltimateFallbackFixup.
83                     yield return CultureInfo.InvariantCulture;
84                     reachedNeutralResourcesCulture = true;
85                     break;
86                 }
87                 yield return currentCulture;
88                 currentCulture = currentCulture.Parent;
89             } while (m_useParents && !currentCulture.HasInvariantCultureName);
90
91             if (!m_useParents || m_startingCulture.HasInvariantCultureName)
92             {
93                 yield break;
94             }
95
96 // Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
97 #if FEATURE_CORECLR
98 #if FEATURE_LEGACYNETCF
99             if(!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
100             {
101 #endif // FEATURE_LEGACYNETCF
102
103                 // 2. user preferred cultures, omitting starting culture if tried already
104                 //    Compat note: For console apps, this API will return cultures like Arabic
105                 //    or Hebrew that are displayed right-to-left.  These don't work with today's 
106                 //    CMD.exe.  Since not all apps can short-circuit RTL languages to look at
107                 //    US English resources, we're exposing an appcompat flag for this, to make the
108                 //    osFallbackArray an empty array, mimicing our V2 behavior.  Apps should instead
109                 //    be using CultureInfo.GetConsoleFallbackUICulture, and then test whether that
110                 //    culture's code page can be displayed on the console, and if not, they should
111                 //    set their culture to their neutral resources language.
112                 //    Note: the app compat switch will omit the OS Preferred fallback culture.
113                 //    Compat note 2:  This feature breaks certain apps dependent on fallback to neutral
114                 //    resources.  See extensive note in GetResourceFallbackArray.  
115                 CultureInfo[] osFallbackArray = LoadPreferredCultures();
116                 if (osFallbackArray != null)
117                 {
118                     foreach (CultureInfo ci in osFallbackArray)
119                     {
120                         // only have to check starting culture and immediate parent for now.
121                         // in Dev10, revisit this policy.
122                         if (m_startingCulture.Name != ci.Name && m_startingCulture.Parent.Name != ci.Name)
123                         {
124                             yield return ci;
125                         }
126                     }
127                 }
128 #if FEATURE_LEGACYNETCF
129             }
130 #endif // FEATURE_LEGACYNETCF
131
132 #endif // FEATURE_CORECLR
133
134             // 3. invariant
135             //    Don't return invariant twice though.
136             if (reachedNeutralResourcesCulture)
137                 yield break;
138
139             yield return CultureInfo.InvariantCulture;
140         }
141
142 // Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
143 #if FEATURE_CORECLR
144         private static CultureInfo[] LoadPreferredCultures()
145         {
146             // The list of preferred cultures includes thread, process, user, and OS
147             // information and may theoretically change every time we call it.  
148             // The caching does save us some allocations - this complexity saved about 
149             // 7% of the wall clock time on a US English machine, and may save more on non-English
150             // boxes (since the fallback list may be longer).
151             String[] cultureNames = GetResourceFallbackArray();
152             if (cultureNames == null)
153                 return null;
154
155             bool useCachedNames = (cachedOsFallbackArray != null && cultureNames.Length == cachedOsFallbackArray.Length);
156             if (useCachedNames)
157             {
158                 for (int i = 0; i < cultureNames.Length; i++)
159                 {
160                     if (!String.Equals(cultureNames[i], cachedOsFallbackArray[i].Name))
161                     {
162                         useCachedNames = false;
163                         break;
164                     }
165                 }
166             }
167             if (useCachedNames)
168                 return cachedOsFallbackArray;
169
170             cachedOsFallbackArray = LoadCulturesFromNames(cultureNames);
171             return cachedOsFallbackArray;
172         }
173
174         private static CultureInfo[] LoadCulturesFromNames(String[] cultureNames)
175         {
176             if (cultureNames == null)
177                 return null;
178
179             CultureInfo[] cultures = new CultureInfo[cultureNames.Length];
180             int culturesIndex = 0;
181             for (int i = 0; i < cultureNames.Length; i++)
182             {
183                 // get cached, read-only cultures to avoid excess allocations
184                 cultures[culturesIndex] = CultureInfo.GetCultureInfo(cultureNames[i]);
185                 // Note GetCultureInfo can return null for a culture name that we don't support on the current OS.
186                 // Don't leave a null in the middle of the array.
187                 if (!Object.ReferenceEquals(cultures[culturesIndex], null))
188                     culturesIndex++;
189             }
190
191             // If we couldn't create a culture, return an array of the right length.
192             if (culturesIndex != cultureNames.Length)
193             {
194                 CultureInfo[] ret = new CultureInfo[culturesIndex];
195                 Array.Copy(cultures, ret, culturesIndex);
196                 cultures = ret;
197             }
198
199             return cultures;
200         }
201
202
203         // Note: May return null.
204         [System.Security.SecuritySafeCritical] // auto-generated
205         private static String[] GetResourceFallbackArray()
206         {
207             // AppCompat note:  We've added this feature for desktop V4 but we ripped it out
208             // before shipping V4.  It shipped in SL 2 and SL 3.  We preserved this behavior in SL 4
209             // for compat with previous Silverlight releases.  We considered re-introducing this in .NET 
210             // 4.5 for Windows 8 but chose not to because the Windows 8 immersive resources model
211             // has been redesigned from the ground up and we chose to support it (for portable libraries
212             // only) instead of further enhancing support for the classic resources model.
213             // ---------------------------------------------------------------------
214             // 
215             // We have an appcompat problem that prevents us from adopting the ideal MUI model for
216             // culture fallback.  Up until .NET Framework v4, our fallback was this:
217             // 
218             // CurrentUICulture & parents   Neutral
219             // 
220             // We also had applications that took a dependency on falling back to neutral resources.
221             // IE, say an app is developed by US English developers - they may include English resources
222             // in the main assembly, not ship an "en" satellite assembly, and ship a French satellite.
223             // They may also omit the NeutralResourcesLanguageAttribute.
224             // 
225             // Starting with Silverlight v2 and following advice from the MUI team, we wanted to call 
226             // the OS's GetThreadPreferredUILanguages, inserting the results like this:
227             //
228             // CurrentUICulture & parents   user-preferred fallback   OS-preferred fallback  Neutral
229             //
230             // This does not fit well for two reasons:
231             //   1) There is no concept of neutral resources in MUI
232             //   2) The user-preferred culture fallbacks make no sense in servers & non-interactive apps
233             // This leads to bad results on certain combinations of OS language installations, user
234             // settings, and applications built in certain styles.  The OS-preferred fallback should
235             // be last, and the user-preferred fallback just breaks certain apps no matter where you put it.
236             // 
237             // Necessary and sufficient conditions for an AppCompat bug (if we respected user & OS fallbacks):
238             //   1) A French OS (ie, you walk into an Internet cafĂ© in Paris)
239             //   2) A .NET application whose neutral resources are authored in English.
240             //   3) The application did not provide an English satellite assembly (a common pattern).
241             //   4) The application is localized to French.
242             //   5) The user wants to read English, expressed in either of two ways:
243             //      a. Changing Windows\92 Display Language in the Regional Options Control Panel
244             //      b. The application explicitly ASKS THE USER what language to display.
245             // 
246             // Obviously the exact languages above can be interchanged a bit - I\92m keeping this concrete.
247             // Also the NeutralResourcesLanguageAttribute will allow this to work, but usually we set it
248             // to en-US for our assemblies, meaning all other English cultures are broken.
249             //
250             // Workarounds:            
251             //   *) Use the NeutralResourcesLanguageAttribute and tell us that your neutral resources 
252             //      are in region-neutral English (en).
253             //   *) Consider shipping a region-neutral English satellite assembly.
254
255             // Future work:
256             // 2) Consider a mechanism for individual assemblies to opt into wanting user-preferred fallback.
257             //    They should ship their neutral resources in a satellite assembly, or use the 
258             //    NeutralResourcesLanguageAttribute to say their neutral resources are in a REGION-NEUTRAL
259             //    language.  An appdomain or process-wide flag may not be sufficient.
260             // 3) Ask Windows to clarify the scenario for the OS preferred fallback list, to decide whether
261             //    we should probe there before or after looking at the neutral resources.  If we move it 
262             //    to after the neutral resources, ask Windows to return a user-preferred fallback list 
263             //    without the OS preferred fallback included.  This is a feature request for 
264             //    GetThreadPreferredUILanguages.  We can muddle through without it by removing the OS 
265             //    preferred fallback cultures from end of the combined user + OS preferred fallback list, carefully.
266             // 4) Do not look at user-preferred fallback if Environment.UserInteractive is false.  (IE, 
267             //    the Windows user who launches ASP.NET shouldn't determine how a web page gets
268             //    localized - the server itself must respect the remote client's requested languages.)
269             // 6) Figure out what should happen in servers (ASP.NET, SQL, NT Services, etc).
270             // 
271             // Done:
272             // 1) Got data from Windows on priority of supporting OS preferred fallback.  We need to do it.
273             //    Helps with consistency w/ Windows, and may be necessary for a long tail of other languages
274             //    (ie, Windows has various degrees of localization support for ~135 languages, and fallbacks
275             //     to certain languages is important.)
276             // 5) Revisited guidance for using the NeutralResourcesLanguageAttribute.  Our docs should now say
277             //    always pick a region-neutral language (ie, "en").
278
279             return CultureInfo.nativeGetResourceFallbackArray();
280         }
281
282 #endif // FEATURE_CORECLR
283     }
284 }