Click here to Skip to main content
15,888,236 members
Articles / Desktop Programming / WPF

Reflection Studio - Part 1 - Introduction: Architecture and Design

Rate me:
Please Sign up or sign in to vote.
4.83/5 (23 votes)
22 Sep 2010GPL36 min read 60K   6.9K   111  
Reflection Studio is a "developer" application for assembly, database, performance, and code generation, written in C# under WPF 4.0.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Mono.Cecil;
using ReflectionStudio.Core.Helpers;
using ReflectionStudio.Core.Reflection.Types;
using System.Collections;

namespace ReflectionStudio.Core.Reflection
{
	public class CecilAssemblyParser : IAssemblyParser
	{
		#region ----------------------CONSTRUCTORS----------------------

		/// <summary>
		/// Constructor
		/// </summary>
		public CecilAssemblyParser()
		{
		}

		#endregion

		#region ----------------------PARSING FUNCTIONS----------------------

		/// <summary>
		/// Load the assembly object only
		/// </summary>
		/// <param name="assemblyPath"></param>
		/// <returns></returns>
		public NetAssembly LoadAssembly(string assemblyPath)
		{
			// get the cecil assembly
			AssemblyDefinition AsmToProfile = CecilHelper.GetAssembly(assemblyPath);
			if (AsmToProfile == null)
				return null;

			return new NetAssembly(AsmToProfile)
			{
				Name = AsmToProfile.Name.Name,
				FilePath = assemblyPath,
				Version = AsmToProfile.Name.Version.ToString(),
				Culture = AsmToProfile.Name.Culture,
				PublicKey = ByteHelper.ByteToHexString(AsmToProfile.Name.PublicKey),
				PublicKeyToken = ByteHelper.ByteToHexString(AsmToProfile.Name.PublicKeyToken),
				Hash = ByteHelper.ByteToHexString(AsmToProfile.Name.Hash),
				HashAlgorithm = AsmToProfile.Name.HashAlgorithm.ToString(),
				EntryPoint = AsmToProfile.EntryPoint != null ? AsmToProfile.EntryPoint.ToString() : string.Empty,
				Kind = AsmToProfile.Kind != null ? AsmToProfile.Kind.ToString() : string.Empty
				//IsProgram = (AsmToProfile.EntryPoint.ToString() != string.Empty && AsmToProfile.Kind.ToString() != string.Empty)
			};
		}

		/// <summary>
		/// Load all object in the given assembly
		/// </summary>
		/// <param name="assemblyPath"></param>
		/// <returns></returns>
		public NetAssembly LoadAssemblyRecursively(string assemblyPath)
		{
			NetAssembly ass = LoadAssembly(assemblyPath);

			//load the references
			ass.References = GetAssemblyReferences(ass);

			//load the type in the assembly
			ass.Children = GetAssemblyTypesRecursively(ass);
			
			return ass;
		}

		/// <summary>
		/// Load the references of the given assembly
		/// </summary>
		/// <param name="assembly"></param>
		/// <returns></returns>
		public ObservableCollection<NetReference> GetAssemblyReferences(NetAssembly assembly)
		{
			ObservableCollection<NetReference> list = new ObservableCollection<NetReference>();

			foreach (ModuleDefinition mod in ((AssemblyDefinition)assembly.Tag).Modules)
			{
				foreach (AssemblyNameReference assRef in mod.AssemblyReferences)
					list.Add(new NetReference(assRef) { Name = assRef.Name } );
			}
			return list;
		}

		/// <summary>
		/// Load all the type in the given assembly
		/// </summary>
		/// <param name="assembly"></param>
		/// <returns></returns>
		public ObservableCollection<BindableObjectExtended> GetAssemblyTypes(NetAssembly assembly)
		{
			ObservableCollection<BindableObjectExtended> list = new ObservableCollection<BindableObjectExtended>();

			//load the type in the assembly
			foreach (TypeDefinition modulType in ((AssemblyDefinition)assembly.Tag).MainModule.Types)
			{
				if (modulType.Name == CecilHelper.CecilModul)
					continue;

				if (modulType.IsClass && !modulType.IsEnum)
				{
					list.Add(AddClass(assembly, modulType));
				}
			}

			return list;
		}

		/// <summary>
		/// Load all the types and subtypes in the given assembly
		/// </summary>
		/// <param name="assembly"></param>
		/// <returns></returns>
		public ObservableCollection<BindableObjectExtended> GetAssemblyTypesRecursively(NetAssembly assembly)
		{
			//get the type in the assemblu
			ObservableCollection<BindableObjectExtended> list = new ObservableCollection<BindableObjectExtended>();
			list = GetAssemblyTypes(assembly);

			//then load type methods
			foreach (NetBaseType typ in list)
			{
				typ.Children = (ObservableCollection<BindableObjectExtended>)GetMembers(typ);
			}

			return list;
		}

		/// <summary>
		/// Get all members in the given type
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		public ObservableCollection<BindableObjectExtended> GetMembers(NetBaseType type)
		{
			ObservableCollection<BindableObjectExtended> list = new ObservableCollection<BindableObjectExtended>();

			TypeDefinition modulType = (TypeDefinition)type.Tag;

			ProcessCecilMethod(modulType.Constructors, type);
			ProcessCecilMethod(modulType.Methods, type);

			foreach (MethodDefinition constructorType in modulType.Constructors)
			{
				list.Add(AddMethod(MethodType.Constructor, constructorType)); 
			}
			foreach (MethodDefinition methodType in modulType.Methods)
			{
				list.Add(new NetMethod(MethodType.Method, methodType) { Name = methodType.Name });
			}

			return list;
		}
		
		#endregion

		#region ----------------------REFRESHING FUNCTIONS----------------------

		/// <summary>
		/// Refresh an assembly and all subtypes if loaded
		/// </summary>
		/// <param name="assembly"></param>
		public void RefreshAssembly(NetAssembly assembly)
		{
			//reload cecil assembly definition
			assembly.Tag = CecilHelper.GetAssembly(assembly.FilePath);

			//for each type in cecil assembly,  refresh or load it
			foreach (TypeDefinition modulType in ((AssemblyDefinition)assembly.Tag).MainModule.Types)
			{
				if (modulType.Name == CecilHelper.CecilModul)
					continue;

				if (modulType.IsClass)
				{
					if (!RefreshClass(assembly.Children, modulType))
						AddClass(assembly, modulType);
				}
			}
		}

		/// <summary>
		/// Refresh a assembly element and all subtypes if loaded
		/// </summary>
		/// <param name="node"></param>
		public void RefreshNode(NetBaseType node)
		{

		}

		#endregion

		#region ----------------------INTERNAL FUNCTIONS----------------------

		private NetClass AddClass(NetAssembly assembly, TypeDefinition modulType)
		{
			return new NetClass(modulType)
			{
				Name = modulType.Name,
				BaseType = new NetBaseType(modulType.BaseType),
				NameSpace = modulType.Namespace,
				Parent = assembly
			};
		}

		private bool RefreshClass(ObservableCollection<BindableObjectExtended> childs, TypeDefinition typeDef)
		{
			if (childs == null || childs.Count <= 0)
				return false;

			//find the type in cecil in our NetClass collection
			foreach (NetClass classTyp in childs)
			{
				if (classTyp.Name == typeDef.FullName)
				{
					classTyp.Tag = typeDef;

					ProcessCecilMethod(typeDef.Constructors, classTyp);
					ProcessCecilMethod(typeDef.Methods, classTyp);

					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Loop through a cecil methods collection
		/// </summary>
		/// <param name="cecilMethods"></param>
		/// <param name="classTyp"></param>
		private void ProcessCecilMethod(CollectionBase cecilMethods, NetBaseType classTyp)
		{
			foreach (MethodDefinition def in cecilMethods)
			{
				if (!RefreshMethod(classTyp.Children, def))
				{
					if( cecilMethods.GetType() == typeof(ConstructorCollection) )
						AddMethod(MethodType.Constructor, def);
					if (cecilMethods.GetType() == typeof(MethodDefinitionCollection))
						AddMethod(MethodType.Method, def);
					if (cecilMethods.GetType() == typeof(PropertyDefinitionCollection))
						AddMethod(MethodType.Property, def);
					if (cecilMethods.GetType() == typeof(EventDefinitionCollection))
						AddMethod(MethodType.Event, def);
					if (cecilMethods.GetType() == typeof(FieldDefinitionCollection))
						AddMethod(MethodType.Field, def);
					if (cecilMethods.GetType() == typeof(InterfaceCollection))
						AddMethod(MethodType.Interface, def);

					//process nested types
					//if (cecilMethods.GetType() == typeof(NestedTypeCollection))
				}
			}
		}

		/// <summary>
		/// Loop through existing class children to remove the ones that does not exist in Cecil
		/// </summary>
		/// <param name="childs"></param>
		private void RemoveClass(ObservableCollection<BindableObjectExtended> childs)
		{
			List<BindableObjectExtended> temp = new List<BindableObjectExtended>();

			//loop on types
			foreach (NetBaseType classTyp in childs)
			{
				//if tag is null, means not founded in cecil
				if (classTyp.Tag == null)
				{
					temp.Add(classTyp);
				}
				else
				{
					RemoveMethods(classTyp.Children);
				}
			}

			//remove them all
			foreach (BindableObjectExtended item in temp)
				childs.Remove(item);
		}

		private NetMethod AddMethod(MethodType typ ,MethodDefinition constructorType)
		{
			return new NetMethod(typ, constructorType)
			{
				Name = constructorType.Name,
				NameSpace = CecilHelper.GetMethodParamSignature(constructorType)
			};
		}

		private bool RefreshMethod(ObservableCollection<BindableObjectExtended> childs, MethodDefinition methodDef)
		{
			foreach (NetMethod methodTyp in childs)
			{
				if (methodTyp.Name == methodDef.Name)
				{
					if (methodTyp.NameSpace == CecilHelper.GetMethodParamSignature(methodDef))
					{
						methodTyp.Tag = methodDef;
						return true;
					}
				}
			}
			return false;
		}

		/// <summary>
		/// Loop through existing methods children to remove the ones that does not exist in Cecil
		/// </summary>
		/// <param name="childs"></param>
		private void RemoveMethods(ObservableCollection<BindableObjectExtended> childs)
		{
			List<BindableObjectExtended> temp = new List<BindableObjectExtended>();

			foreach (NetMethod methodTyp in childs)
			{
				if (methodTyp.Tag == null)
				{
					temp.Add(methodTyp);
				}
			}

			foreach (BindableObjectExtended item in temp)
				childs.Remove(item);
		}

		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Architect
France France
WPF and MVVM fan, I practice C # in all its forms from the beginning of the NET Framework without mentioning C ++ / MFC and other software packages such as databases, ASP, WCF, Web & Windows services, Application, and now Core and UWP.
In my wasted hours, I am guilty of having fathered C.B.R. and its cousins C.B.R. for WinRT and UWP on the Windows store.
But apart from that, I am a great handyman ... the house, a rocket stove to heat the jacuzzi and the last one: a wood oven for pizza, bread, and everything that goes inside

https://guillaumewaser.wordpress.com/
https://fouretcompagnie.wordpress.com/

Comments and Discussions