Click here to Skip to main content
15,888,286 members
Articles / Programming Languages / Forth.NET

DocMounter 2: A tool to build VS.NET documentation (now with Sandcastle)

,
Rate me:
Please Sign up or sign in to vote.
4.94/5 (29 votes)
15 Nov 2010GPL314 min read 138.9K   1.4K   99  
Tool for creating MS Visual Studio documentation files - XML Summaries, HxS/MSHC help solutions and manuals.
using System;
using System.Collections;
using System.Reflection;
using System.Windows.Forms;

namespace TenTec.Utils
{
	/// <summary>
	/// A class to get lists of members from assemblies using reflection
	/// </summary>
	internal class ReflectionPopulateManager
	{
		#region Fields
		TreeView fTreeView;
		Assembly[] fAssemblies;
		static Hashtable fControlsAttached;
		int fMaxLevel;
		#endregion

		#region Common
		/// <summary>
		/// Populates the specified tree view with the data from the 
		/// specified assemblies. The tree view is not populated at 
		/// once, it is populated "just in time" when it is needed.
		/// </summary>
		/// <param name="maxLevel">
		/// Specify 0 if only namespaces should be displayed; 1 - if 
		/// only types.
		/// </param>
		public static void Attach(TreeView treeView, Assembly[] assemblies, DelegateFilterMember filter, int maxLevel, bool showReferencedAssemblies)
		{
			#region Check the arguments
			if(treeView == null)
				throw new ArgumentNullException("ManagerPopulateReflection.Attach(treeView)");
			if(assemblies == null)
				throw new ArgumentNullException("ManagerPopulateReflection.Attach(assemblies)");
			if(assemblies.Length == 0)
				throw new ArgumentException("ManagerPopulateReflection.Attach(assemblies)");
			foreach(Assembly myAssembly in assemblies)
				if(myAssembly == null)
					throw new ArgumentException("ManagerPopulateReflection.Attach(assemblies)");
			if(fControlsAttached != null && fControlsAttached.ContainsKey(treeView))
				throw new Exception("ManagerPopulateReflection.Attach: The tree view has already been attached.");
			#endregion

			ReflectionPopulateManager myInstance = new ReflectionPopulateManager(treeView, assemblies, filter, maxLevel, showReferencedAssemblies);

			AdjustControlsAttached();
			fControlsAttached.Add(treeView, myInstance);

			treeView.ImageList = HelpNodeImagesManager.ImageList;
		}

		/// <summary>
		/// Cleans up the specified tree view and deattaches all the 
		/// event handlers attached in the Attach method.
		/// </summary>
		public static void Detach(TreeView treeView)
		{
			#region Check the arguments
			if(treeView == null)
				throw new ArgumentNullException("ManagerPopulateReflection.Detach(treeView)");
			if(fControlsAttached == null || !fControlsAttached.ContainsKey(treeView))
				throw new Exception("ManagerPopulateReflection.Detach: The tree view has not been attached.");
			#endregion

			treeView.Nodes.Clear();
			treeView.BeforeExpand -= new TreeViewCancelEventHandler((fControlsAttached[treeView] as ReflectionPopulateManager).fTreeView_BeforeExpand);
			treeView.ImageList = null;
			fControlsAttached.Remove(treeView);
		}

		/// <summary>
		/// Checks whether the fControlsAttached field initialized,
		/// and initializes it if needed.
		/// </summary>
		private static void AdjustControlsAttached()
		{
			if (fControlsAttached == null)
				fControlsAttached = new Hashtable();
		}

		/// <summary>
		/// Creates a new instance of the ManagerPopulateReflection class.
		/// </summary>
		/// <param name="maxLevel">
		/// Specify 0 if only namespaces should be displayed; 1 - if 
		/// only types.
		/// </param>
		private ReflectionPopulateManager(TreeView treeView, Assembly[] assemblies, DelegateFilterMember filter, int maxLevel, bool showReferencedAssemblies)
		{
			#region Check the arguments
			if(treeView == null)
				throw new ArgumentNullException("ManagerPopulateReflection.#ctor(treeView)");
			if(assemblies == null)
				throw new ArgumentNullException("ManagerPopulateReflection.#ctor(assemblies)");
			if(assemblies.Length == 0)
				throw new ArgumentException("ManagerPopulateReflection.#ctor(assemblies)");
			foreach(Assembly myAssembly in assemblies)
				if(myAssembly == null)
					throw new ArgumentException("ManagerPopulateReflection.#ctor(assemblies)");
			if(maxLevel < 0)
				throw new ArgumentOutOfRangeException("ManagerPopulateReflection.#ctor(maxLevel)");
			#endregion

			#region Initialize fields
			fTreeView = treeView;
			fAssemblies = assemblies;
			if(filter != null)
				Filter += filter;
			fMaxLevel = maxLevel;
			#endregion

			fTreeView.BeforeExpand += new TreeViewCancelEventHandler(fTreeView_BeforeExpand);

			AddNamespaces(assemblies, showReferencedAssemblies);
		}

		/// <summary>
		/// Adds namespace nodes to the tree view.
		/// </summary>
		private void AddNamespaces(Assembly[] assemblies, bool showReferencedAssemblies)
		{
			fTreeView.BeginUpdate();
#if !DEBUG
			try
			{	
#endif
				foreach(Assembly myAssembly in assemblies)
				{
					AddAssemblyNamespaces(myAssembly);

					#region Process the referenced assemblies
					if (showReferencedAssemblies)
					{
						AssemblyName[] myNames = myAssembly.GetReferencedAssemblies();
						foreach (AssemblyName myName in myNames)
						{
#if !DEBUG
						try
						{
#endif
							AddAssemblyNamespaces(Assembly.Load(myName));
#if !DEBUG
						}
						catch(Exception ex)
						{
							MessagesManager.ShowError(fTreeView.FindForm(), ex.Message);
						}
#endif
						}
					}
					#endregion
				}
#if !DEBUG
			}
			catch(Exception ex)
			{
				MessagesManager.ShowError(fTreeView.FindForm(), ex.Message);
			}
			finally
			{
#endif
				fTreeView.EndUpdate();
#if !DEBUG
			}
#endif
		}

		/// <summary>
		/// Adds namespace nodes to the tree view.
		/// </summary>
		private void AddAssemblyNamespaces(Assembly assembly)
		{
			Type[] myTypes = assembly.GetTypes();
			for(int myIndexType = 0; myIndexType < myTypes.Length; myIndexType ++)
			{
				Type myType = myTypes[myIndexType];
				if(ReflectionManager.IsExternal(myType))
				{
					if(Filter != null)
						if(!Filter(myType))
							continue;

					string myNamespace = myType.Namespace;
					if(GetNodeNamespace(myNamespace) == null)
						AddNodeNamespace(myNamespace);
				}
			}
		}

		private void AddNodeNamespace(string @namespace)
		{
			TreeNode myNode = new TreeNodeReflectionMember(@namespace);
			if (fMaxLevel > 0)
			{
				myNode.Tag = null;
				myNode.Nodes.Add(string.Empty);
				fTreeView.Nodes.Add(myNode);
			}
		}

		private TreeNode GetNodeType(Type type)
		{
			TreeNode myNodeNamespace = GetNodeNamespace(type.Namespace);
			if(myNodeNamespace == null)
				return null;

			for(int myIndexNode = myNodeNamespace.Nodes.Count-1; myIndexNode >= 0; myIndexNode--)
			{
				TreeNodeReflectionMember myNode = myNodeNamespace.Nodes[myIndexNode] as TreeNodeReflectionMember;
				if(myNode != null && myNode.Member == type)
					return myNode;
			}

			return null;
		}

		private TreeNode GetNodeNamespace(string @namespace)
		{
			@namespace = DisplayLabelManager.GetLabel(@namespace);
			for(int myIndexNode = fTreeView.Nodes.Count-1; myIndexNode >= 0; myIndexNode--)
			{
				TreeNodeReflectionMember myNode = fTreeView.Nodes[myIndexNode] as TreeNodeReflectionMember;
				if(myNode != null && myNode.NamespaceLabel == @namespace)
					return myNode;
			}
			return null;
		}

		/// <summary>
		/// Adds nodes for each of the types contained in the
		/// specified namespace.
		/// </summary>
		private void AddNodesNamespaceTypes(TreeNodeReflectionMember node)
		{
			#region Check the arguments
			if(node == null)
				throw new ArgumentNullException("ManagerPopulateReflection.AddNodesNamespaceTypes(node)");
			if(!node.IsNamespace)
				throw new ArgumentException("ManagerPopulateReflection.AddNodesNamespaceTypes(node)");
			#endregion

			string myNamespace = DisplayLabelManager.GetNamespace(node.NamespaceLabel);

			fTreeView.BeginUpdate();
			node.Nodes.Clear();
			try
			{
				foreach(Assembly myAssembly in fAssemblies)
				{
					AddNodesNamespaceTypes(node, myAssembly, myNamespace);

					#region Process the references assemblies
					AssemblyName[] myNames = myAssembly.GetReferencedAssemblies();
					foreach(AssemblyName myName in myNames)
					{
						try
						{
							AddNodesNamespaceTypes(node, Assembly.Load(myName), myNamespace);
						}
						catch(Exception ex)
						{
							MessagesManager.ShowError(fTreeView.FindForm(), ex.Message);
						}
					}
					#endregion
				}
			}
			catch(Exception ex)
			{
				MessagesManager.ShowError(fTreeView.FindForm(), ex.Message);
			}
			finally
			{
				fTreeView.EndUpdate();
			}
		}

		/// <summary>
		/// Adds nodes for each of the types contained in the
		/// specified namespace in the specified assembly.
		/// </summary>
		/// <param name="node">
		/// The node which corresponds to the specified namespace.
		/// </param>
		private void AddNodesNamespaceTypes(TreeNode node, Assembly assembly, string @namespace)
		{
			foreach(Type myType in assembly.GetTypes())
			{
				if(ReflectionManager.IsExternal(myType))
				{
					if(Filter != null)
						if(!Filter(myType))
							continue;

					if(myType.Namespace == @namespace)
					{
						if(GetNodeType(myType) != null)
							continue;

						TreeNode myNode = CreateNodeType(myType);
						if(!ReflectionManager.IsDelegate(myType))
						{
							if(fMaxLevel > 1)
							{
								myNode.Tag = null;
								myNode.Nodes.Add(string.Empty);
							}
						}
						node.Nodes.Add(myNode);
					}
				}
			}
		}

		private TreeNodeReflectionMember CreateNodeType(Type type)
		{
			return new TreeNodeReflectionMember(type);
		}

		private TreeNodeReflectionMember CreateNodeEvent(EventInfo @event)
		{
			return new TreeNodeReflectionMember(@event);
		}

		private TreeNodeReflectionMember CreateNodeField(FieldInfo field)
		{
			return new TreeNodeReflectionMember(field);
		}

		private TreeNodeReflectionMember CreateNodeMethod(MethodBase method)
		{
			return new TreeNodeReflectionMember(method);
		}

		private TreeNodeReflectionMember CreateNodeProp(PropertyInfo prop)
		{
			return new TreeNodeReflectionMember(prop);
		}

		private void AddNodesMemberMembers(TreeNodeReflectionMember node)
		{
			if(node == null)
				throw new ArgumentNullException("ManagerPopulateReflection.AddNodesMemberMembers(node)");
			if(!node.IsMember)
				throw new ArgumentException("ManagerPopulateReflection.AddNodesMemberMembers(node)");
			fTreeView.BeginUpdate();
			node.Nodes.Clear();
			try
			{
				Type myType = node.Member as Type;
				if(myType != null)
					AddNodesTypeMembers(node, myType);
			}
			catch(Exception ex)
			{
				MessagesManager.ShowError(fTreeView.FindForm(), ex.Message);
			}
			fTreeView.EndUpdate();
		}

		private void AddNodesTypeMembers(TreeNode node, Type type)
		{
			if(type == null)
				throw new ArgumentNullException("ManagerPopulateReflection.AddNodesTypeMembers(node)");
			MemberInfo[] myMembers = type.GetMembers(ReflectionManager.GetBindingFlags());
			foreach(MemberInfo myMember in myMembers)
			{
				if(Filter != null)
					if(!Filter(myMember))
						continue;
				ProjectNodeTypes myMemberType = ReflectionManager.GetMemberType(myMember);
				TreeNodeReflectionMember myNode = null;
				switch(myMemberType)
				{
					case ProjectNodeTypes.Event:
						EventInfo myEvent = myMember as EventInfo;
						if(ReflectionManager.IsExternal(myEvent))
							myNode = CreateNodeEvent(myEvent);
						break;
					case ProjectNodeTypes.Field:
					case ProjectNodeTypes.EnumField:
						FieldInfo myField = myMember as FieldInfo;
						if(ReflectionManager.IsExternal(myField))
							myNode = CreateNodeField(myField);
						break;
					case ProjectNodeTypes.Method:
						MethodBase myMethod = myMember as MethodBase;
						if(ReflectionManager.IsExternal(myMethod))
						{
							myNode = CreateNodeMethod(myMethod);
							myNode.Tag = null;
						}
						break;
					case ProjectNodeTypes.Property:
						PropertyInfo myProp = myMember as PropertyInfo;
						if(ReflectionManager.IsExternal(myProp))
							myNode = CreateNodeProp(myProp);
						break;
				}
				if(myNode != null)
					node.Nodes.Add(myNode);
			}
		}

		private void fTreeView_BeforeExpand(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
		{
			if(e.Node.Tag != null)
				return;
			TreeNodeReflectionMember myNode = e.Node as TreeNodeReflectionMember;
			if(myNode.IsNamespace)
				AddNodesNamespaceTypes(myNode);
			else if(myNode.IsMember)
				AddNodesMemberMembers(myNode);
			e.Node.Tag = true;
		}

		private DelegateFilterMember Filter;

		#endregion
	}

	internal class TreeNodeReflectionMember: TreeNode
	{
		#region Fields
		MemberInfo fMember;
		string fNamespaceLabel;
		#endregion

		#region Methods
		public TreeNodeReflectionMember(MemberInfo member)
		{
			if(member == null)
				throw new ArgumentNullException("TreeNodeReflectionMember.#ctor(member)");
			fMember = member;
			bool myAddReflectedType = member.MemberType == System.Reflection.MemberTypes.NestedType;
			Text = DisplayLabelManager.GetLabel(member, false, myAddReflectedType);
			ImageIndex = SelectedImageIndex = HelpNodeImagesManager.GetProjectNodeTypeImageIndex(ReflectionManager.GetMemberType(member));
		}
		public TreeNodeReflectionMember(string @namespace)
		{
			fNamespaceLabel = DisplayLabelManager.GetLabel(@namespace);
			Text = fNamespaceLabel;
			ImageIndex = SelectedImageIndex = HelpNodeImagesManager.GetProjectNodeTypeImageIndex(ProjectNodeTypes.Namespace);
		}
		#endregion

		#region Properties
		public MemberInfo Member
		{
			get
			{
				return fMember;
			}
		}
		public string NamespaceLabel
		{
			get
			{
				return fNamespaceLabel;
			}
		}
		public bool IsNamespace
		{
			get
			{
				return fNamespaceLabel != null;
			}
		}
		public bool IsMember
		{
			get
			{
				return fMember != null;
			}
		}
		#endregion
	}

	internal delegate bool DelegateFilterMember(MemberInfo member);

}

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
Software Developer (Senior)
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Written By
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.
This is a Organisation

2 members

Comments and Discussions