Skip to content

Commit

Permalink
Make ResourceManager.BaseName work as documented. (#75497)
Browse files Browse the repository at this point in the history
* Make ResourceManager.BaseName work as documented.

This property is documented to return the "qualified namespace name and
the root resource name of a resource file". However, previously when
constructing a ResourceManager with a Type the BaseName property would
instead return the name of the type without its namespace.

fix #74918

* Remove _locationInfo field

* Add additional test cases for ResourceManager.BaseName.

Address feedback from #75497 (comment).

* Fix BaseName test case in response to #75497 (comment)
  • Loading branch information
madelson committed Nov 3, 2022
1 parent bdf97c3 commit ce406f1
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void Ctor_Default()
public void Ctor_Type(Type type)
{
var resourceManager = new ComponentResourceManager(type);
Assert.Equal("Int32", resourceManager.BaseName);
Assert.Equal("System.Int32", resourceManager.BaseName);
Assert.False(resourceManager.IgnoreCase);
Assert.NotNull(resourceManager.ResourceSetType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ public FileBasedResourceGroveler(ResourceManager.ResourceManagerMediator mediato
// If we've hit top of the Culture tree, return.
if (culture.HasInvariantCultureName)
{
// We really don't think this should happen - we always
// expect the neutral locale's resources to be present.
string? locationInfo = _mediator.LocationInfo == null ? "<null>" : _mediator.LocationInfo.FullName;
throw new MissingManifestResourceException($"{SR.MissingManifestResource_NoNeutralDisk}{Environment.NewLineConst}baseName: {_mediator.BaseNameField} locationInfo: {locationInfo} fileName: {_mediator.GetResourceFileName(culture)}");
throw new MissingManifestResourceException($"{SR.MissingManifestResource_NoNeutralDisk}{Environment.NewLineConst}baseName: {_mediator.BaseNameField} fileName: {_mediator.GetResourceFileName(culture)}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,43 +321,36 @@ private static ResourceSet InternalGetResourceSetFromSerializedData(Stream store
return rs;
}

private Stream? GetManifestResourceStream(Assembly satellite, string fileName)
private static Stream? GetManifestResourceStream(Assembly satellite, string fileName)
{
Debug.Assert(satellite != null, "satellite shouldn't be null; check caller");
Debug.Assert(fileName != null, "fileName shouldn't be null; check caller");

return satellite.GetManifestResourceStream(_mediator.LocationInfo!, fileName) ??
return satellite.GetManifestResourceStream(fileName) ??
CaseInsensitiveManifestResourceStreamLookup(satellite, fileName);
}

// Looks up a .resources file in the assembly manifest using
// case-insensitive lookup rules. Yes, this is slow. The metadata
// dev lead refuses to make all assembly manifest resource lookups case-insensitive,
// even optionally case-insensitive.
private Stream? CaseInsensitiveManifestResourceStreamLookup(Assembly satellite, string name)
private static Stream? CaseInsensitiveManifestResourceStreamLookup(Assembly satellite, string name)
{
Debug.Assert(satellite != null, "satellite shouldn't be null; check caller");
Debug.Assert(name != null, "name shouldn't be null; check caller");

string? nameSpace = _mediator.LocationInfo?.Namespace;

char c = Type.Delimiter;
string resourceName = nameSpace != null && name != null ?
string.Concat(nameSpace, new ReadOnlySpan<char>(in c), name) :
string.Concat(nameSpace, name);

string? canonicalName = null;
foreach (string existingName in satellite.GetManifestResourceNames())
{
if (string.Equals(existingName, resourceName, StringComparison.InvariantCultureIgnoreCase))
if (string.Equals(existingName, name, StringComparison.InvariantCultureIgnoreCase))
{
if (canonicalName == null)
{
canonicalName = existingName;
}
else
{
throw new MissingManifestResourceException(SR.Format(SR.MissingManifestResource_MultipleBlobs, resourceName, satellite.ToString()));
throw new MissingManifestResourceException(SR.Format(SR.MissingManifestResource_MultipleBlobs, name, satellite.ToString()));
}
}
}
Expand Down Expand Up @@ -488,16 +481,10 @@ private void HandleResourceStreamMissing(string fileName)
const string MesgFailFast = System.CoreLib.Name + ResourceManager.ResFileExtension + " couldn't be found! Large parts of the BCL won't work!";
System.Environment.FailFast(MesgFailFast);
}
// We really don't think this should happen - we always
// expect the neutral locale's resources to be present.
string resName = string.Empty;
if (_mediator.LocationInfo != null && _mediator.LocationInfo.Namespace != null)
resName = _mediator.LocationInfo.Namespace + Type.Delimiter;
resName += fileName;
Debug.Assert(_mediator.MainAssembly != null);
throw new MissingManifestResourceException(
SR.Format(SR.MissingManifestResource_NoNeutralAsm,
resName, _mediator.MainAssembly.GetName().Name, GetManifestResourceNamesList(_mediator.MainAssembly)));
fileName, _mediator.MainAssembly.GetName().Name, GetManifestResourceNamesList(_mediator.MainAssembly)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ internal sealed class CultureNameResourceSetPair

private Dictionary<string, ResourceSet>? _resourceSets;
private readonly string? _moduleDir; // For assembly-ignorant directory location
private readonly Type? _locationInfo; // For Assembly or type-based directory layout

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
private readonly Type? _userResourceSet; // Which ResourceSet instance to create
Expand Down Expand Up @@ -227,9 +226,13 @@ public ResourceManager(Type resourceSource)
if (resourceSource is not RuntimeType)
throw new ArgumentException(SR.Argument_MustBeRuntimeType);

_locationInfo = resourceSource;
MainAssembly = _locationInfo.Assembly;
BaseNameField = resourceSource.Name;
MainAssembly = resourceSource.Assembly;

string? nameSpace = resourceSource.Namespace;
char c = Type.Delimiter;
BaseNameField = nameSpace is null
? resourceSource.Name
: string.Concat(nameSpace, new ReadOnlySpan<char>(in c), resourceSource.Name);

CommonAssemblyInit();
}
Expand Down Expand Up @@ -407,7 +410,7 @@ protected virtual string GetResourceFileName(CultureInfo culture)
{
string fileName = GetResourceFileName(culture);
Debug.Assert(MainAssembly != null);
Stream? stream = MainAssembly.GetManifestResourceStream(_locationInfo!, fileName);
Stream? stream = MainAssembly.GetManifestResourceStream(fileName);
if (createIfNotExists && stream != null)
{
rs = ((ManifestBasedResourceGroveler)_resourceGroveler).CreateResourceSet(stream, MainAssembly);
Expand Down Expand Up @@ -747,9 +750,6 @@ internal ResourceManagerMediator(ResourceManager rm)
// NEEDED ONLY BY FILE-BASED
internal string? ModuleDir => _rm._moduleDir;

// NEEDED BOTH BY FILE-BASED AND ASSEMBLY-BASED
internal Type? LocationInfo => _rm._locationInfo;

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
internal Type? UserResourceSet => _rm._userResourceSet;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,19 @@ public static void UsingResourceSet()
[Fact]
public static void BaseName()
{
var manager = new ResourceManager("System.Resources.Tests.Resources.TestResx", typeof(ResourceManagerTests).GetTypeInfo().Assembly);
var manager = new ResourceManager("System.Resources.Tests.Resources.TestResx", typeof(ResourceManagerTests).Assembly);
Assert.Equal("System.Resources.Tests.Resources.TestResx", manager.BaseName);

manager = new ResourceManager(typeof(System.Resources.Tests.Resources.TestResx));
Assert.Equal("System.Resources.Tests.Resources.TestResx", manager.BaseName);

Type typeWithoutNamespace = new { }.GetType();
Assert.Null(typeWithoutNamespace.Namespace);
manager = new ResourceManager(typeWithoutNamespace);
Assert.Equal(typeWithoutNamespace.Name, manager.BaseName);

manager = new ResourceManager(typeof(List<string>));
Assert.Equal("System.Collections.Generic.List`1", manager.BaseName);
}

[Theory]
Expand Down

0 comments on commit ce406f1

Please sign in to comment.