|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Windows.Baml2006;
using System.Xaml;
using MS.Internal;
using MS.Utility;
namespace System.Windows.Markup
{
public partial class XamlTypeMapper
{
internal class XamlTypeMapperSchemaContext : XamlSchemaContext
{
// Initialized in constructor
Dictionary<string, FrugalObjectList<string>> _nsDefinitions;
XamlTypeMapper _typeMapper;
WpfSharedXamlSchemaContext _sharedSchemaContext;
// Lock on syncObject
readonly object syncObject = new object();
Dictionary<string, string> _piNamespaces;
IEnumerable<string> _allXamlNamespaces;
Dictionary<Type, XamlType> _allowedInternalTypes;
HashSet<string> _clrNamespaces;
internal XamlTypeMapperSchemaContext(XamlTypeMapper typeMapper)
: base()
{
_typeMapper = typeMapper;
_sharedSchemaContext = (WpfSharedXamlSchemaContext)XamlReader.GetWpfSchemaContext();
// Copy all the NamespaceMapEntrys from our parent into _nsDefinitions
if (typeMapper._namespaceMaps != null)
{
_nsDefinitions = new Dictionary<string, FrugalObjectList<string>>();
foreach (NamespaceMapEntry mapEntry in _typeMapper._namespaceMaps)
{
FrugalObjectList<string> clrNsList;
if (!_nsDefinitions.TryGetValue(mapEntry.XmlNamespace, out clrNsList))
{
clrNsList = new FrugalObjectList<string>(1);
_nsDefinitions.Add(mapEntry.XmlNamespace, clrNsList);
}
string clrNs = GetClrNsUri(mapEntry.ClrNamespace, mapEntry.AssemblyName);
clrNsList.Add(clrNs);
}
}
// Copy all the PIs from our parent into _piNamespaces
if (typeMapper.PITable.Count > 0)
{
_piNamespaces = new Dictionary<string, string>(typeMapper.PITable.Count);
foreach (DictionaryEntry entry in typeMapper.PITable)
{
ClrNamespaceAssemblyPair pair = (ClrNamespaceAssemblyPair)entry.Value;
string clrNs = GetClrNsUri(pair.ClrNamespace, pair.AssemblyName);
_piNamespaces.Add((string)entry.Key, clrNs);
}
}
_clrNamespaces = new HashSet<string>();
}
// Return all the namespaces gathered via reflection, plus any additional ones from user mappings
public override IEnumerable<string> GetAllXamlNamespaces()
{
IEnumerable<string> result = _allXamlNamespaces;
if (result == null)
{
lock (syncObject)
{
if (_nsDefinitions != null || _piNamespaces != null)
{
// Use shared schema context, to avoid redundant reflection
List<string> resultList = new List<string>(_sharedSchemaContext.GetAllXamlNamespaces());
AddKnownNamespaces(resultList);
result = resultList.AsReadOnly();
}
else
{
result = _sharedSchemaContext.GetAllXamlNamespaces();
}
_allXamlNamespaces = result;
}
}
return result;
}
public override XamlType GetXamlType(Type type)
{
if (ReflectionHelper.IsPublicType(type))
{
return _sharedSchemaContext.GetXamlType(type);
}
return GetInternalType(type, null);
}
public override bool TryGetCompatibleXamlNamespace(string xamlNamespace, out string compatibleNamespace)
{
// XmlnsCompat mappings only come from reflection, so get them from the shared SchemaContext
if (_sharedSchemaContext.TryGetCompatibleXamlNamespace(xamlNamespace, out compatibleNamespace))
{
return true;
}
// Otherwise, if we have a map or a PI for the namespace, then treat it as known
if (_nsDefinitions != null && _nsDefinitions.ContainsKey(xamlNamespace) ||
_piNamespaces != null && SyncContainsKey(_piNamespaces, xamlNamespace))
{
compatibleNamespace = xamlNamespace;
return true;
}
return false;
}
// Returns a Hashtable of ns -> NamespaceMapEntry[] containing all mappings that are
// different from what would be returned by the shared context.
// We don't use _typeMapper.NamespaceMapHashList because we don't want to duplicate all
// the reflection done by the shared SchemaContext.
// Thread safety: we assume this is called on the parser thread, and so it's safe to
// iterate the _typeMapper's data structures.
internal Hashtable GetNamespaceMapHashList()
{
// Copy the ctor-provided namespace mappings into the result
Hashtable result = new Hashtable();
if (_typeMapper._namespaceMaps != null)
{
foreach (NamespaceMapEntry mapEntry in _typeMapper._namespaceMaps)
{
NamespaceMapEntry clone = new NamespaceMapEntry
{
XmlNamespace = mapEntry.XmlNamespace,
ClrNamespace = mapEntry.ClrNamespace,
AssemblyName = mapEntry.AssemblyName,
AssemblyPath = mapEntry.AssemblyPath
};
AddToMultiHashtable(result, mapEntry.XmlNamespace, clone);
}
}
// Copy the Mapping PIs into the result
foreach (DictionaryEntry piEntry in _typeMapper.PITable)
{
ClrNamespaceAssemblyPair mapping = (ClrNamespaceAssemblyPair)piEntry.Value;
NamespaceMapEntry mapEntry = new NamespaceMapEntry
{
XmlNamespace = (string)piEntry.Key,
ClrNamespace = mapping.ClrNamespace,
AssemblyName = mapping.AssemblyName,
AssemblyPath = _typeMapper.AssemblyPathFor(mapping.AssemblyName)
};
AddToMultiHashtable(result, mapEntry.XmlNamespace, mapEntry);
}
// Add any clr-namespaces that were resolved using custom assembly paths
lock (syncObject)
{
foreach (string clrNs in _clrNamespaces)
{
string clrNamespace, assembly;
SplitClrNsUri(clrNs, out clrNamespace, out assembly);
if (!string.IsNullOrEmpty(assembly))
{
string assemblyPath = _typeMapper.AssemblyPathFor(assembly);
if (!string.IsNullOrEmpty(assemblyPath))
{
NamespaceMapEntry mapEntry = new NamespaceMapEntry
{
XmlNamespace = clrNs,
ClrNamespace = clrNamespace,
AssemblyName = assembly,
AssemblyPath = assemblyPath
};
AddToMultiHashtable(result, mapEntry.XmlNamespace, mapEntry);
}
}
}
}
// Convert all the lists to arrays
object[] keys = new object[result.Count];
result.Keys.CopyTo(keys, 0);
foreach (object key in keys)
{
List<NamespaceMapEntry> list = (List<NamespaceMapEntry>)result[key];
result[key] = list.ToArray();
}
return result;
}
internal void SetMappingProcessingInstruction(string xamlNamespace, ClrNamespaceAssemblyPair pair)
{
string clrNs = GetClrNsUri(pair.ClrNamespace, pair.AssemblyName);
lock (syncObject)
{
if (_piNamespaces == null)
{
_piNamespaces = new Dictionary<string, string>();
}
_piNamespaces[xamlNamespace] = clrNs;
// We potentially have a new namespace, so invalidate the cached list of namespaces
_allXamlNamespaces = null;
}
}
protected override XamlType GetXamlType(string xamlNamespace, string name, params XamlType[] typeArguments)
{
try
{
return LookupXamlType(xamlNamespace, name, typeArguments);
}
catch (Exception e)
{
if (CriticalExceptions.IsCriticalException(e))
{
throw;
}
if (_typeMapper.LoadReferenceAssemblies())
{
// If new reference assemblies were loaded, retry the type load
return LookupXamlType(xamlNamespace, name, typeArguments);
}
else
{
throw;
}
}
}
// Load assembly using the user-provided path, if available
protected override Assembly OnAssemblyResolve(string assemblyName)
{
string assemblyPath = _typeMapper.AssemblyPathFor(assemblyName);
if (!string.IsNullOrEmpty(assemblyPath))
{
return ReflectionHelper.LoadAssembly(assemblyName, assemblyPath);
}
return base.OnAssemblyResolve(assemblyName);
}
private static string GetClrNsUri(string clrNamespace, string assembly)
{
return XamlReaderHelper.MappingProtocol + clrNamespace +
XamlReaderHelper.MappingAssembly + assembly;
}
private static void SplitClrNsUri(string xmlNamespace, out string clrNamespace, out string assembly)
{
clrNamespace = null;
assembly = null;
int clrNsIndex = xmlNamespace.IndexOf(XamlReaderHelper.MappingProtocol, StringComparison.Ordinal);
if (clrNsIndex < 0)
{
return;
}
clrNsIndex += XamlReaderHelper.MappingProtocol.Length;
if (clrNsIndex <= xmlNamespace.Length)
{
return;
}
int assemblyIndex = xmlNamespace.IndexOf(XamlReaderHelper.MappingAssembly, StringComparison.Ordinal);
if (assemblyIndex < clrNsIndex)
{
clrNamespace = xmlNamespace.Substring(clrNsIndex);
return;
}
clrNamespace = xmlNamespace.Substring(clrNsIndex, assemblyIndex - clrNsIndex);
assemblyIndex += XamlReaderHelper.MappingAssembly.Length;
if (assemblyIndex <= xmlNamespace.Length)
{
return;
}
assembly = xmlNamespace.Substring(assemblyIndex);
}
// Should be called within lock (syncObject)
private void AddKnownNamespaces(List<string> nsList)
{
if (_nsDefinitions != null)
{
foreach (string ns in _nsDefinitions.Keys)
{
if (!nsList.Contains(ns))
{
nsList.Add(ns);
}
}
}
if (_piNamespaces != null)
{
foreach (string ns in _piNamespaces.Keys)
{
if (!nsList.Contains(ns))
{
nsList.Add(ns);
}
}
}
}
private XamlType GetInternalType(Type type, XamlType sharedSchemaXamlType)
{
lock (syncObject)
{
if (_allowedInternalTypes == null)
{
_allowedInternalTypes = new Dictionary<Type, XamlType>();
}
XamlType result;
if (!_allowedInternalTypes.TryGetValue(type, out result))
{
WpfSharedXamlSchemaContext.RequireRuntimeType(type);
if (_typeMapper.IsInternalTypeAllowedInFullTrust(type))
{
// Return a type that claims to be public, so that XXR doesn't filter it out.
result = new VisibilityMaskingXamlType(type, _sharedSchemaContext);
}
else
{
result = sharedSchemaXamlType ?? _sharedSchemaContext.GetXamlType(type);
}
_allowedInternalTypes.Add(type, result);
}
return result;
}
}
// Tries to use the user-provided mappings to resolve a type. Note that the returned type
// won't include the mapping in its list of namespaces; that is not necessary for v3 compat.
//
// Note that for anything other than reflected mappings, we don't use the shared SchemaContext,
// because we want to make sure that our OnAssemblyResolve handler is called.
private XamlType LookupXamlType(string xamlNamespace, string name, XamlType[] typeArguments)
{
// First look through the user-provided namespace mappings
XamlType result;
FrugalObjectList<string> clrNsList;
if (_nsDefinitions != null && _nsDefinitions.TryGetValue(xamlNamespace, out clrNsList))
{
for (int i = 0; i < clrNsList.Count; i++)
{
result = base.GetXamlType(clrNsList[i], name, typeArguments);
if (result != null)
{
return result;
}
}
}
// Then look for a PI
string piMappingClrNs;
if (_piNamespaces != null && SyncTryGetValue(_piNamespaces, xamlNamespace, out piMappingClrNs))
{
return base.GetXamlType(piMappingClrNs, name, typeArguments);
}
// Then see if it's a CLR namespace
if (xamlNamespace.StartsWith(XamlReaderHelper.MappingProtocol, StringComparison.Ordinal))
{
lock (syncObject)
{
if (!_clrNamespaces.Contains(xamlNamespace))
{
_clrNamespaces.Add(xamlNamespace);
}
}
return base.GetXamlType(xamlNamespace, name, typeArguments);
}
// Finally, use the reflected xmlnsdefs (get them from the shared SchemaContext, to avoid redundant reflection)
result = _sharedSchemaContext.GetXamlTypeInternal(xamlNamespace, name, typeArguments);
// Apply visibility filtering, because the shared SchemaContext can't do that.
return result == null || result.IsPublic ? result : GetInternalType(result.UnderlyingType, result);
}
private bool SyncContainsKey<K,V>(IDictionary<K, V> dict, K key)
{
lock (syncObject)
{
return dict.ContainsKey(key);
}
}
private bool SyncTryGetValue(IDictionary<string, string> dict, string key, out string value)
{
lock (syncObject)
{
return dict.TryGetValue(key, out value);
}
}
private static void AddToMultiHashtable<K, V>(Hashtable hashtable, K key, V value)
{
List<V> list = (List<V>)hashtable[key];
if (list == null)
{
list = new List<V>();
hashtable.Add(key, list);
}
list.Add(value);
}
}
// This XamlType claims to be public, so that XXR won't filter out internals that are
// allowed by XamlTypeMapper.AllowInternalType
private class VisibilityMaskingXamlType : XamlType
{
public VisibilityMaskingXamlType(Type underlyingType, XamlSchemaContext schemaContext)
: base(underlyingType, schemaContext)
{
}
protected override bool LookupIsPublic()
{
return true;
}
}
}
}
|