// 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 { /// /// Allows finding types in a set of assemblies. /// public class XamlTypeFinder : ICloneable { public XamlTypeFinder() { _registeredAssemblies = new List(); } sealed class AssemblyNamespaceMapping : IEquatable { 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 ClrNamespaces = new List(); internal XamlNamespace Clone() { XamlNamespace copy = new XamlNamespace(this.XmlNamespacePrefix, this.XmlNamespace); // AssemblyNamespaceMapping is immutable copy.ClrNamespaces.AddRange(this.ClrNamespaces); return copy; } } Dictionary namespaces = new Dictionary(); Dictionary reverseDict = new Dictionary(); Dictionary> reverseDictList = new Dictionary>(); /// /// Gets a type referenced in XAML. /// /// The XML namespace to use to look up the type. /// This can be a registered namespace or a 'clr-namespace' value. /// The local name of the type to find. /// /// The requested type, or null if it could not be found. /// 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; } /// /// Gets the XML namespace that can be used for the specified assembly/namespace combination. /// 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; } } /// /// Gets the XML namespaces that can be used for the specified assembly/namespace combination. /// public List GetXmlNamespacesFor(Assembly assembly, string @namespace, bool getClrNamespace = false) { AssemblyNamespaceMapping mapping = new AssemblyNamespaceMapping(assembly, @namespace); List xmlNamespaces; if (!getClrNamespace && reverseDictList.TryGetValue(mapping, out xmlNamespaces)) { return xmlNamespaces; } else { return new List() { "clr-namespace:" + mapping.Namespace + ";assembly=" + mapping.Assembly.GetName().Name }; } } /// /// Gets the prefix to use for the specified XML namespace, /// or null if no suitable prefix could be found. /// 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 xmlNamespaceList; if (reverseDictList.TryGetValue(mapping, out xmlNamespaceList)) { if (!xmlNamespaceList.Contains(ns.XmlNamespace)) xmlNamespaceList.Add(ns.XmlNamespace); } else reverseDictList.Add(mapping, new List(){ ns.XmlNamespace }); string xmlNamespace; if (reverseDict.TryGetValue(mapping, out xmlNamespace)) { if (xmlNamespace == XamlConstants.PresentationNamespace) { return; } } reverseDict[mapping] = ns.XmlNamespace; } private List _registeredAssemblies; public ReadOnlyCollection RegisteredAssemblies { get { return new ReadOnlyCollection(_registeredAssemblies); } } /// /// Registers XAML namespaces defined in the for lookup. /// public void RegisterAssembly(Assembly assembly) { if (assembly == null) throw new ArgumentNullException("assembly"); _registeredAssemblies.Add(assembly); Dictionary namespacePrefixes = new Dictionary(); 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)); } } } } /// /// Register the Namspaces not found in any Assembly, but used by VS and Expression Blend /// 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)); } /// /// Load the assembly with the specified name. /// You can override this method to implement custom assembly lookup. /// public virtual Assembly LoadAssembly(string name) { return Assembly.Load(name); } /// /// Clones this XamlTypeFinder. /// public virtual XamlTypeFinder Clone() { XamlTypeFinder copy = new XamlTypeFinder(); copy.ImportFrom(this); return copy; } /// /// Import information from another XamlTypeFinder. /// Use this if you override Clone(). /// protected void ImportFrom(XamlTypeFinder source) { if (source == null) throw new ArgumentNullException("source"); _registeredAssemblies.AddRange(source.RegisteredAssemblies); foreach (KeyValuePair pair in source.namespaces) { this.namespaces.Add(pair.Key, pair.Value.Clone()); } foreach (KeyValuePair pair in source.reverseDict) { this.reverseDict.Add(pair.Key, pair.Value); } foreach (KeyValuePair> pair in source.reverseDictList) { this.reverseDictList.Add(pair.Key, pair.Value.ToList()); } } object ICloneable.Clone() { return this.Clone(); } /// /// Creates a new XamlTypeFinder where the WPF namespaces are registered. /// public static XamlTypeFinder CreateWpfTypeFinder() { return WpfTypeFinder.Instance.Clone(); } /// /// Converts the specified to local. /// 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 } } } }