mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							161 lines
						
					
					
						
							5.7 KiB
						
					
					
				
			
		
		
	
	
							161 lines
						
					
					
						
							5.7 KiB
						
					
					
				// Copyright (c) AlphaSierraPapa for the SharpDevelop Team | 
						|
// This code is distributed under the MS-PL (for details please see \doc\MS-PL.txt) | 
						|
 | 
						|
using System; | 
						|
using System.Collections.Generic; | 
						|
using System.IO; | 
						|
using System.Linq; | 
						|
using System.Threading; | 
						|
using System.Threading.Tasks; | 
						|
using System.Xml.Linq; | 
						|
 | 
						|
using ICSharpCode.AvalonEdit.Highlighting; | 
						|
using ICSharpCode.Decompiler.Util; | 
						|
using ICSharpCode.ILSpy; | 
						|
using ICSharpCode.ILSpy.TextView; | 
						|
using ICSharpCode.ILSpy.TreeNodes; | 
						|
using Mono.Cecil; | 
						|
using Ricciolo.StylesExplorer.MarkupReflection; | 
						|
 | 
						|
namespace ILSpy.BamlDecompiler | 
						|
{ | 
						|
	public sealed class BamlResourceEntryNode : ResourceEntryNode | 
						|
	{ | 
						|
		public BamlResourceEntryNode(string key, Stream data) : base(key, data) | 
						|
		{ | 
						|
		} | 
						|
		 | 
						|
		public override bool View(DecompilerTextView textView) | 
						|
		{ | 
						|
			IHighlightingDefinition highlighting = null; | 
						|
			 | 
						|
			textView.RunWithCancellation( | 
						|
				token => Task.Factory.StartNew( | 
						|
					() => { | 
						|
						AvalonEditTextOutput output = new AvalonEditTextOutput(); | 
						|
						try { | 
						|
							if (LoadBaml(output, token)) | 
						|
								highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); | 
						|
						} catch (Exception ex) { | 
						|
							output.Write(ex.ToString()); | 
						|
						} | 
						|
						return output; | 
						|
					}, token)) | 
						|
				.Then(output => textView.ShowNode(output, this, highlighting)) | 
						|
				.HandleExceptions(); | 
						|
			return true; | 
						|
		} | 
						|
		 | 
						|
		bool LoadBaml(AvalonEditTextOutput output, CancellationToken cancellationToken) | 
						|
		{ | 
						|
			var asm = this.Ancestors().OfType<AssemblyTreeNode>().FirstOrDefault().LoadedAssembly; | 
						|
			Data.Position = 0; | 
						|
			XDocument xamlDocument = LoadIntoDocument(asm.GetAssemblyResolver(), asm.GetAssemblyDefinitionAsync().Result, Data, cancellationToken); | 
						|
			output.Write(xamlDocument.ToString()); | 
						|
			return true; | 
						|
		} | 
						|
 | 
						|
		internal static XDocument LoadIntoDocument(IAssemblyResolver resolver, AssemblyDefinition asm, Stream stream, CancellationToken cancellationToken) | 
						|
		{ | 
						|
			cancellationToken.ThrowIfCancellationRequested(); | 
						|
			XDocument xamlDocument; | 
						|
			using (XmlBamlReader reader = new XmlBamlReader(stream, new CecilTypeResolver(resolver, asm))) { | 
						|
				xamlDocument = XDocument.Load(reader); | 
						|
				ConvertConnectionIds(xamlDocument, asm, cancellationToken); | 
						|
				ConvertToEmptyElements(xamlDocument.Root); | 
						|
				MoveNamespacesToRoot(xamlDocument, reader.XmlnsDefinitions); | 
						|
				return xamlDocument; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		static void ConvertConnectionIds(XDocument xamlDocument, AssemblyDefinition asm, CancellationToken cancellationToken) | 
						|
		{ | 
						|
			var attr = xamlDocument.Root.Attribute(XName.Get("Class", XmlBamlReader.XWPFNamespace)); | 
						|
			if (attr != null) { | 
						|
				string fullTypeName = attr.Value; | 
						|
				var mappings = new ConnectMethodDecompiler(asm).DecompileEventMappings(fullTypeName, cancellationToken); | 
						|
				RemoveConnectionIds(xamlDocument.Root, mappings); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		class XAttributeComparer : IEqualityComparer<XAttribute> | 
						|
		{ | 
						|
			public bool Equals(XAttribute x, XAttribute y) | 
						|
			{ | 
						|
				if (ReferenceEquals(x, y)) | 
						|
					return true; | 
						|
				if (x == null || y == null) | 
						|
					return false; | 
						|
				return x.ToString() == y.ToString(); | 
						|
			} | 
						|
 | 
						|
			public int GetHashCode(XAttribute obj) | 
						|
			{ | 
						|
				return obj.ToString().GetHashCode(); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		static void MoveNamespacesToRoot(XDocument xamlDocument, IEnumerable<XmlNamespace> missingXmlns) | 
						|
		{ | 
						|
			var additionalXmlns = new HashSet<XAttribute>(new XAttributeComparer()) { | 
						|
				new XAttribute("xmlns", XmlBamlReader.DefaultWPFNamespace), | 
						|
				new XAttribute(XName.Get("x", XNamespace.Xmlns.NamespaceName), XmlBamlReader.XWPFNamespace) | 
						|
			}; | 
						|
 | 
						|
			additionalXmlns.AddRange( | 
						|
				missingXmlns | 
						|
					.Where(ns => !string.IsNullOrWhiteSpace(ns.Prefix)) | 
						|
					.Select(ns => new XAttribute(XName.Get(ns.Prefix, XNamespace.Xmlns.NamespaceName), ns.Namespace)) | 
						|
			); | 
						|
			 | 
						|
			foreach (var element in xamlDocument.Root.DescendantsAndSelf()) { | 
						|
				if (element.Name.NamespaceName != XmlBamlReader.DefaultWPFNamespace && !additionalXmlns.Any(ka => ka.Value == element.Name.NamespaceName)) { | 
						|
					string newPrefix = new string(element.Name.LocalName.Where(c => char.IsUpper(c)).ToArray()).ToLowerInvariant(); | 
						|
					int current = additionalXmlns.Count(ka => ka.Name.Namespace == XNamespace.Xmlns && ka.Name.LocalName.TrimEnd(ch => char.IsNumber(ch)) == newPrefix); | 
						|
					if (current > 0) | 
						|
						newPrefix += (current + 1).ToString(); | 
						|
					XName defaultXmlns = XName.Get(newPrefix, XNamespace.Xmlns.NamespaceName); | 
						|
					if (element.Name.NamespaceName != XmlBamlReader.DefaultWPFNamespace) | 
						|
						additionalXmlns.Add(new XAttribute(defaultXmlns, element.Name.NamespaceName)); | 
						|
				} | 
						|
			} | 
						|
			 | 
						|
			foreach (var xmlns in additionalXmlns.Except(xamlDocument.Root.Attributes())) { | 
						|
				xamlDocument.Root.Add(xmlns); | 
						|
			} | 
						|
		} | 
						|
		 | 
						|
		static void ConvertToEmptyElements(XElement element) | 
						|
		{ | 
						|
			foreach (var el in element.Elements()) { | 
						|
				if (!el.IsEmpty && !el.HasElements && el.Value == "") { | 
						|
					el.RemoveNodes(); | 
						|
					continue; | 
						|
				} | 
						|
				ConvertToEmptyElements(el); | 
						|
			} | 
						|
		} | 
						|
		 | 
						|
		static void RemoveConnectionIds(XElement element, List<(LongSet key, EventRegistration[] value)> eventMappings) | 
						|
		{ | 
						|
			foreach (var child in element.Elements()) | 
						|
				RemoveConnectionIds(child, eventMappings); | 
						|
			 | 
						|
			var removableAttrs = new List<XAttribute>(); | 
						|
			var addableAttrs = new List<XAttribute>(); | 
						|
			foreach (var attr in element.Attributes(XName.Get("ConnectionId", XmlBamlReader.XWPFNamespace))) { | 
						|
				int id, index; | 
						|
				if (int.TryParse(attr.Value, out id) && (index = eventMappings.FindIndex(item => item.key.Contains(id))) > -1) { | 
						|
					foreach (var entry in eventMappings[index].value) { | 
						|
						string xmlns = ""; // TODO : implement xmlns resolver! | 
						|
						addableAttrs.Add(new XAttribute(xmlns + entry.EventName, entry.MethodName)); | 
						|
					} | 
						|
					removableAttrs.Add(attr); | 
						|
				} | 
						|
			} | 
						|
			foreach (var attr in removableAttrs) | 
						|
				attr.Remove(); | 
						|
			element.Add(addableAttrs); | 
						|
		} | 
						|
	} | 
						|
} |