/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using dnlib.DotNet; using dnSpy.AsmEditor.Commands; using dnSpy.AsmEditor.Resources; using dnSpy.Contracts.Documents; using dnSpy.Contracts.Documents.TreeView; using dnSpy.Contracts.Documents.TreeView.Resources; namespace dnSpy.AsmEditor.Compiler { interface IAddUpdatedNodesHelperProvider { AddUpdatedNodesHelper Create(ModuleDocumentNode modNode, ModuleImporter importer); } [Export(typeof(IAddUpdatedNodesHelperProvider))] sealed class AddUpdatedNodesHelperProvider : IAddUpdatedNodesHelperProvider { readonly Lazy methodAnnotations; readonly Lazy resourceNodeFactory; readonly IDocumentTreeView documentTreeView; [ImportingConstructor] AddUpdatedNodesHelperProvider(Lazy methodAnnotations, Lazy resourceNodeFactory, IDocumentTreeView documentTreeView) { this.methodAnnotations = methodAnnotations; this.resourceNodeFactory = resourceNodeFactory; this.documentTreeView = documentTreeView; } public AddUpdatedNodesHelper Create(ModuleDocumentNode modNode, ModuleImporter importer) => new AddUpdatedNodesHelper(methodAnnotations, resourceNodeFactory, documentTreeView, modNode, importer); } sealed class AddUpdatedNodesHelper { readonly AssemblyDocumentNode? asmNode; readonly ModuleDocumentNode modNode; readonly TypeNodeCreator[] newTypeNodeCreators; readonly ResourceNodeCreator? resourceNodeCreator; readonly ExistingTypeNodeUpdater[] existingTypeNodeUpdaters; readonly DeclSecurity[]? newAssemblyDeclSecurities; readonly DeclSecurity[]? origAssemblyDeclSecurities; readonly CustomAttribute[]? newAssemblyCustomAttributes; readonly CustomAttribute[]? newModuleCustomAttributes; readonly CustomAttribute[]? origAssemblyCustomAttributes; readonly CustomAttribute[]? origModuleCustomAttributes; readonly ExportedType[]? newExportedTypes; readonly ExportedType[]? origExportedTypes; readonly Version? newAssemblyVersion; readonly Version? origAssemblyVersion; public AddUpdatedNodesHelper(Lazy methodAnnotations, Lazy resourceNodeFactory, IDocumentTreeView documentTreeView, ModuleDocumentNode modNode, ModuleImporter importer) { asmNode = modNode.TreeNode.Parent?.Data as AssemblyDocumentNode; this.modNode = modNode; var dict = new Dictionary>(StringComparer.Ordinal); foreach (var t in importer.NewNonNestedTypes) { var ns = (t.TargetType!.Namespace ?? UTF8String.Empty).String; if (!dict.TryGetValue(ns, out var list)) dict[ns] = list = new List(); list.Add(t.TargetType); } newTypeNodeCreators = dict.Values.Select(a => new TypeNodeCreator(modNode, a)).ToArray(); existingTypeNodeUpdaters = importer.MergedNonNestedTypes.Select(a => new ExistingTypeNodeUpdater(methodAnnotations, modNode, a)).ToArray(); if (!importer.MergedNonNestedTypes.All(a => a.TargetType!.Module == modNode.Document.ModuleDef)) throw new InvalidOperationException(); newAssemblyDeclSecurities = importer.NewAssemblyDeclSecurities; newAssemblyCustomAttributes = importer.NewAssemblyCustomAttributes; newModuleCustomAttributes = importer.NewModuleCustomAttributes; newExportedTypes = importer.NewExportedTypes; newAssemblyVersion = importer.NewAssemblyVersion; if (newAssemblyDeclSecurities is not null) origAssemblyDeclSecurities = modNode.Document.AssemblyDef?.DeclSecurities.ToArray(); if (newAssemblyCustomAttributes is not null) origAssemblyCustomAttributes = modNode.Document.AssemblyDef?.CustomAttributes.ToArray(); if (newModuleCustomAttributes is not null) origModuleCustomAttributes = modNode.Document.ModuleDef!.CustomAttributes.ToArray(); if (newExportedTypes is not null) origExportedTypes = modNode.Document.ModuleDef!.ExportedTypes.ToArray(); if (newAssemblyVersion is not null) origAssemblyVersion = modNode.Document.AssemblyDef?.Version; if (importer.NewResources!.Length != 0) { var module = modNode.Document.ModuleDef!; var rsrcListNode = GetResourceListTreeNode(modNode); Debug2.Assert(rsrcListNode is not null); if (rsrcListNode is not null) { var newNodes = new NodeAndResource[importer.NewResources.Length]; var treeNodeGroup = documentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceTreeNodeGroup); for (int i = 0; i < newNodes.Length; i++) { var resource = importer.NewResources[i]; var node = (DocumentTreeNodeData)documentTreeView.TreeView.Create(resourceNodeFactory.Value.Create(module, resource, treeNodeGroup)).Data; newNodes[i] = new NodeAndResource(node); } resourceNodeCreator = new ResourceNodeCreator(rsrcListNode, newNodes); } } } static ResourcesFolderNode? GetResourceListTreeNode(ModuleDocumentNode modNode) { modNode.TreeNode.EnsureChildrenLoaded(); return modNode.TreeNode.DataChildren.OfType().FirstOrDefault(); } public void Execute() { bool refresh = false; for (int i = 0; i < newTypeNodeCreators.Length; i++) newTypeNodeCreators[i].Add(); for (int i = 0; i < existingTypeNodeUpdaters.Length; i++) existingTypeNodeUpdaters[i].Add(); if (origAssemblyDeclSecurities is not null && newAssemblyDeclSecurities is not null) { modNode.Document.AssemblyDef!.DeclSecurities.Clear(); foreach (var ds in newAssemblyDeclSecurities) modNode.Document.AssemblyDef.DeclSecurities.Add(ds); } if (origAssemblyCustomAttributes is not null && newAssemblyCustomAttributes is not null) { modNode.Document.AssemblyDef!.CustomAttributes.Clear(); foreach (var ca in newAssemblyCustomAttributes) modNode.Document.AssemblyDef.CustomAttributes.Add(ca); } if (origModuleCustomAttributes is not null && newModuleCustomAttributes is not null) { modNode.Document.ModuleDef!.CustomAttributes.Clear(); foreach (var ca in newModuleCustomAttributes) modNode.Document.ModuleDef.CustomAttributes.Add(ca); } if (origExportedTypes is not null && newExportedTypes is not null) { modNode.Document.ModuleDef!.ExportedTypes.Clear(); foreach (var et in newExportedTypes) modNode.Document.ModuleDef.ExportedTypes.Add(et); } if (newAssemblyVersion is not null && origAssemblyVersion is not null) { modNode.Document.AssemblyDef!.Version = newAssemblyVersion; refresh = true; } resourceNodeCreator?.Add(); if (refresh) asmNode?.TreeNode.RefreshUI(); } public void Undo() { bool refresh = false; resourceNodeCreator?.Remove(); if (newAssemblyVersion is not null && origAssemblyVersion is not null) { modNode.Document.AssemblyDef!.Version = origAssemblyVersion; refresh = true; } if (origExportedTypes is not null && newExportedTypes is not null) { modNode.Document.ModuleDef!.ExportedTypes.Clear(); foreach (var et in origExportedTypes) modNode.Document.ModuleDef.ExportedTypes.Add(et); } if (origModuleCustomAttributes is not null && newModuleCustomAttributes is not null) { modNode.Document.ModuleDef!.CustomAttributes.Clear(); foreach (var ca in origModuleCustomAttributes) modNode.Document.ModuleDef.CustomAttributes.Add(ca); } if (origAssemblyCustomAttributes is not null && newAssemblyCustomAttributes is not null) { modNode.Document.AssemblyDef!.CustomAttributes.Clear(); foreach (var ca in origAssemblyCustomAttributes) modNode.Document.AssemblyDef.CustomAttributes.Add(ca); } if (origAssemblyDeclSecurities is not null && newAssemblyDeclSecurities is not null) { modNode.Document.AssemblyDef!.DeclSecurities.Clear(); foreach (var ds in origAssemblyDeclSecurities) modNode.Document.AssemblyDef.DeclSecurities.Add(ds); } for (int i = existingTypeNodeUpdaters.Length - 1; i >= 0; i--) existingTypeNodeUpdaters[i].Remove(); for (int i = newTypeNodeCreators.Length - 1; i >= 0; i--) newTypeNodeCreators[i].Remove(); if (refresh) asmNode?.TreeNode.RefreshUI(); } public IEnumerable ModifiedObjects { get { yield return modNode; } } } }