356 строки
12 KiB
C#
356 строки
12 KiB
C#
// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
// software and associated documentation files (the "Software"), to deal in the Software
|
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all copies or
|
|
// substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Windows;
|
|
using System.Windows.Markup;
|
|
using System.Xaml;
|
|
using XamlReader = System.Windows.Markup.XamlReader;
|
|
|
|
namespace ICSharpCode.WpfDesign.XamlDom
|
|
{
|
|
/// <summary>
|
|
/// Allows finding types in a set of assemblies.
|
|
/// </summary>
|
|
public class XamlTypeFinder : ICloneable
|
|
{
|
|
public XamlTypeFinder()
|
|
{
|
|
_registeredAssemblies = new List<Assembly>();
|
|
}
|
|
|
|
sealed class AssemblyNamespaceMapping : IEquatable<AssemblyNamespaceMapping>
|
|
{
|
|
internal readonly Assembly Assembly;
|
|
internal readonly string Namespace;
|
|
|
|
internal AssemblyNamespaceMapping(Assembly assembly, string @namespace)
|
|
{
|
|
this.Assembly = assembly;
|
|
this.Namespace = @namespace;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return Assembly.GetHashCode() ^ Namespace.GetHashCode();
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return Equals(obj as AssemblyNamespaceMapping);
|
|
}
|
|
|
|
public bool Equals(AssemblyNamespaceMapping other)
|
|
{
|
|
return other != null && other.Assembly == this.Assembly && other.Namespace == this.Namespace;
|
|
}
|
|
}
|
|
|
|
sealed class XamlNamespace
|
|
{
|
|
internal readonly string XmlNamespacePrefix;
|
|
internal readonly string XmlNamespace;
|
|
|
|
internal XamlNamespace(string xmlNamespacePrefix, string xmlNamespace)
|
|
{
|
|
this.XmlNamespacePrefix = xmlNamespacePrefix;
|
|
this.XmlNamespace = xmlNamespace;
|
|
}
|
|
|
|
internal List<AssemblyNamespaceMapping> ClrNamespaces = new List<AssemblyNamespaceMapping>();
|
|
|
|
internal XamlNamespace Clone()
|
|
{
|
|
XamlNamespace copy = new XamlNamespace(this.XmlNamespacePrefix, this.XmlNamespace);
|
|
// AssemblyNamespaceMapping is immutable
|
|
copy.ClrNamespaces.AddRange(this.ClrNamespaces);
|
|
return copy;
|
|
}
|
|
}
|
|
|
|
Dictionary<string, XamlNamespace> namespaces = new Dictionary<string, XamlNamespace>();
|
|
Dictionary<AssemblyNamespaceMapping, string> reverseDict = new Dictionary<AssemblyNamespaceMapping, string>();
|
|
Dictionary<AssemblyNamespaceMapping, List<string>> reverseDictList = new Dictionary<AssemblyNamespaceMapping, List<string>>();
|
|
|
|
/// <summary>
|
|
/// Gets a type referenced in XAML.
|
|
/// </summary>
|
|
/// <param name="xmlNamespace">The XML namespace to use to look up the type.
|
|
/// This can be a registered namespace or a 'clr-namespace' value.</param>
|
|
/// <param name="localName">The local name of the type to find.</param>
|
|
/// <returns>
|
|
/// The requested type, or null if it could not be found.
|
|
/// </returns>
|
|
public Type GetType(string xmlNamespace, string localName)
|
|
{
|
|
if (xmlNamespace == null)
|
|
throw new ArgumentNullException("xmlNamespace");
|
|
if (localName == null)
|
|
throw new ArgumentNullException("localName");
|
|
XamlNamespace ns;
|
|
if (!namespaces.TryGetValue(xmlNamespace, out ns)) {
|
|
if (xmlNamespace.StartsWith("clr-namespace:", StringComparison.Ordinal)) {
|
|
ns = namespaces[xmlNamespace] = ParseNamespace(xmlNamespace);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
foreach (AssemblyNamespaceMapping mapping in ns.ClrNamespaces) {
|
|
Type type = mapping.Assembly.GetType(mapping.Namespace + "." + localName);
|
|
if (type != null)
|
|
return type;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the XML namespace that can be used for the specified assembly/namespace combination.
|
|
/// </summary>
|
|
public string GetXmlNamespaceFor(Assembly assembly, string @namespace, bool getClrNamespace = false)
|
|
{
|
|
AssemblyNamespaceMapping mapping = new AssemblyNamespaceMapping(assembly, @namespace);
|
|
string xmlNamespace;
|
|
if (!getClrNamespace && reverseDict.TryGetValue(mapping, out xmlNamespace)) {
|
|
return xmlNamespace;
|
|
} else {
|
|
return "clr-namespace:" + mapping.Namespace + ";assembly=" + mapping.Assembly.GetName().Name;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the XML namespaces that can be used for the specified assembly/namespace combination.
|
|
/// </summary>
|
|
public List<string> GetXmlNamespacesFor(Assembly assembly, string @namespace, bool getClrNamespace = false)
|
|
{
|
|
AssemblyNamespaceMapping mapping = new AssemblyNamespaceMapping(assembly, @namespace);
|
|
List<string> xmlNamespaces;
|
|
if (!getClrNamespace && reverseDictList.TryGetValue(mapping, out xmlNamespaces)) {
|
|
return xmlNamespaces;
|
|
} else {
|
|
return new List<string>() { "clr-namespace:" + mapping.Namespace + ";assembly=" + mapping.Assembly.GetName().Name };
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the prefix to use for the specified XML namespace,
|
|
/// or null if no suitable prefix could be found.
|
|
/// </summary>
|
|
public string GetPrefixForXmlNamespace(string xmlNamespace)
|
|
{
|
|
XamlNamespace ns;
|
|
|
|
if (namespaces.TryGetValue(xmlNamespace, out ns)) {
|
|
return ns.XmlNamespacePrefix;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
XamlNamespace ParseNamespace(string xmlNamespace)
|
|
{
|
|
string name = xmlNamespace;
|
|
Debug.Assert(name.StartsWith("clr-namespace:", StringComparison.Ordinal));
|
|
name = name.Substring("clr-namespace:".Length);
|
|
string namespaceName, assembly;
|
|
int pos = name.IndexOf(';');
|
|
if (pos < 0) {
|
|
namespaceName = name;
|
|
assembly = "";
|
|
} else {
|
|
namespaceName = name.Substring(0, pos);
|
|
name = name.Substring(pos + 1).Trim();
|
|
if (!name.StartsWith("assembly=", StringComparison.Ordinal)) {
|
|
throw new XamlLoadException("Expected: 'assembly='");
|
|
}
|
|
assembly = name.Substring("assembly=".Length);
|
|
}
|
|
XamlNamespace ns = new XamlNamespace(null, xmlNamespace);
|
|
|
|
Assembly asm = LoadAssembly(assembly);
|
|
|
|
if (asm == null && assembly == "mscorlib")
|
|
asm = typeof (Boolean).Assembly;
|
|
|
|
if (asm != null) {
|
|
AddMappingToNamespace(ns, new AssemblyNamespaceMapping(asm, namespaceName));
|
|
}
|
|
return ns;
|
|
}
|
|
|
|
void AddMappingToNamespace(XamlNamespace ns, AssemblyNamespaceMapping mapping)
|
|
{
|
|
ns.ClrNamespaces.Add(mapping);
|
|
|
|
List<string> xmlNamespaceList;
|
|
if (reverseDictList.TryGetValue(mapping, out xmlNamespaceList)) {
|
|
if (!xmlNamespaceList.Contains(ns.XmlNamespace))
|
|
xmlNamespaceList.Add(ns.XmlNamespace);
|
|
}
|
|
else
|
|
reverseDictList.Add(mapping, new List<string>(){ ns.XmlNamespace });
|
|
|
|
string xmlNamespace;
|
|
if (reverseDict.TryGetValue(mapping, out xmlNamespace)) {
|
|
if (xmlNamespace == XamlConstants.PresentationNamespace) {
|
|
return;
|
|
}
|
|
}
|
|
reverseDict[mapping] = ns.XmlNamespace;
|
|
}
|
|
|
|
private List<Assembly> _registeredAssemblies;
|
|
|
|
public ReadOnlyCollection<Assembly> RegisteredAssemblies
|
|
{
|
|
get
|
|
{
|
|
return new ReadOnlyCollection<Assembly>(_registeredAssemblies);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers XAML namespaces defined in the <paramref name="assembly"/> for lookup.
|
|
/// </summary>
|
|
public void RegisterAssembly(Assembly assembly)
|
|
{
|
|
if (assembly == null)
|
|
throw new ArgumentNullException("assembly");
|
|
|
|
_registeredAssemblies.Add(assembly);
|
|
|
|
Dictionary<string, string> namespacePrefixes = new Dictionary<string, string>();
|
|
foreach (XmlnsPrefixAttribute xmlnsPrefix in assembly.GetCustomAttributes(typeof(XmlnsPrefixAttribute), true)) {
|
|
namespacePrefixes.Add(xmlnsPrefix.XmlNamespace, xmlnsPrefix.Prefix);
|
|
}
|
|
|
|
foreach (XmlnsDefinitionAttribute xmlnsDef in assembly.GetCustomAttributes(typeof(XmlnsDefinitionAttribute), true)) {
|
|
XamlNamespace ns;
|
|
if (!namespaces.TryGetValue(xmlnsDef.XmlNamespace, out ns)) {
|
|
string prefix;
|
|
namespacePrefixes.TryGetValue(xmlnsDef.XmlNamespace, out prefix);
|
|
ns = namespaces[xmlnsDef.XmlNamespace] = new XamlNamespace(prefix, xmlnsDef.XmlNamespace);
|
|
}
|
|
if (string.IsNullOrEmpty(xmlnsDef.AssemblyName)) {
|
|
AddMappingToNamespace(ns, new AssemblyNamespaceMapping(assembly, xmlnsDef.ClrNamespace));
|
|
} else {
|
|
Assembly asm = LoadAssembly(xmlnsDef.AssemblyName);
|
|
if (asm != null) {
|
|
AddMappingToNamespace(ns, new AssemblyNamespaceMapping(asm, xmlnsDef.ClrNamespace));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register the Namspaces not found in any Assembly, but used by VS and Expression Blend
|
|
/// </summary>
|
|
public void RegisterDesignerNamespaces()
|
|
{
|
|
var ns = namespaces[XamlConstants.DesignTimeNamespace] = new XamlNamespace("d", XamlConstants.DesignTimeNamespace);
|
|
AddMappingToNamespace(ns, new AssemblyNamespaceMapping(typeof(DesignTimeProperties).Assembly, typeof(DesignTimeProperties).Namespace));
|
|
ns = namespaces[XamlConstants.MarkupCompatibilityNamespace] = new XamlNamespace("mc", XamlConstants.MarkupCompatibilityNamespace);
|
|
AddMappingToNamespace(ns, new AssemblyNamespaceMapping(typeof(MarkupCompatibilityProperties).Assembly, typeof(MarkupCompatibilityProperties).Namespace));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the assembly with the specified name.
|
|
/// You can override this method to implement custom assembly lookup.
|
|
/// </summary>
|
|
public virtual Assembly LoadAssembly(string name)
|
|
{
|
|
return Assembly.Load(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clones this XamlTypeFinder.
|
|
/// </summary>
|
|
public virtual XamlTypeFinder Clone()
|
|
{
|
|
XamlTypeFinder copy = new XamlTypeFinder();
|
|
copy.ImportFrom(this);
|
|
return copy;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Import information from another XamlTypeFinder.
|
|
/// Use this if you override Clone().
|
|
/// </summary>
|
|
protected void ImportFrom(XamlTypeFinder source)
|
|
{
|
|
if (source == null)
|
|
throw new ArgumentNullException("source");
|
|
|
|
_registeredAssemblies.AddRange(source.RegisteredAssemblies);
|
|
|
|
foreach (KeyValuePair<string, XamlNamespace> pair in source.namespaces) {
|
|
this.namespaces.Add(pair.Key, pair.Value.Clone());
|
|
}
|
|
foreach (KeyValuePair<AssemblyNamespaceMapping, string> pair in source.reverseDict) {
|
|
this.reverseDict.Add(pair.Key, pair.Value);
|
|
}
|
|
foreach (KeyValuePair<AssemblyNamespaceMapping, List<string>> pair in source.reverseDictList) {
|
|
this.reverseDictList.Add(pair.Key, pair.Value.ToList());
|
|
}
|
|
}
|
|
|
|
object ICloneable.Clone()
|
|
{
|
|
return this.Clone();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new XamlTypeFinder where the WPF namespaces are registered.
|
|
/// </summary>
|
|
public static XamlTypeFinder CreateWpfTypeFinder()
|
|
{
|
|
return WpfTypeFinder.Instance.Clone();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the specified <see cref="Uri"/> to local.
|
|
/// </summary>
|
|
public virtual Uri ConvertUriToLocalUri(Uri uri)
|
|
{
|
|
return uri;
|
|
}
|
|
|
|
static class WpfTypeFinder
|
|
{
|
|
internal static readonly XamlTypeFinder Instance;
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline",
|
|
Justification = "We're using an explicit constructor to get it's lazy-loading semantics.")]
|
|
static WpfTypeFinder()
|
|
{
|
|
Instance = new XamlTypeFinder();
|
|
Instance.RegisterDesignerNamespaces();
|
|
Instance.RegisterAssembly(typeof(Point).Assembly); // WindowsBase
|
|
Instance.RegisterAssembly(typeof(IAddChild).Assembly); // PresentationCore
|
|
Instance.RegisterAssembly(typeof(XamlReader).Assembly); // PresentationFramework
|
|
Instance.RegisterAssembly(typeof(XamlType).Assembly); // System.Xaml
|
|
Instance.RegisterAssembly(typeof(Type).Assembly); // mscorelib
|
|
}
|
|
}
|
|
}
|
|
}
|