Click here to Skip to main content
15,892,809 members
Articles / Programming Languages / C#

Building a Refactoring Plug-in for VS.NET - the Sequel

Rate me:
Please Sign up or sign in to vote.
4.70/5 (31 votes)
31 Aug 20047 min read 158.2K   1.9K   95  
Describes how to extend the original refactor add-in with additional features.
using System;
using Extensibility;
using EnvDTE;
using System.Collections;

namespace RefactorAddIn
{
	/// <summary>
	/// Move a selected function to the superclass
	/// </summary>
	public class MoveToSuperClass : RefactorAddIn.VSPlugIn
	{
		public MoveToSuperClass(_DTE applicationObject, AddIn addInInstance)
			: base(applicationObject, addInInstance)
		{		}
		#region private vars
		CodeElement element2Move = null;
		CodeFunction function2Move = null;
		CodeProperty property2Move = null;
		CodeVariable Variable2Move = null;
		CodeClass targetClass = null;
		CodeClass sourceClass = null;
		#endregion

		#region overrides
		public override string longDescription
		{
			get
			{
				return "Move the selected item into the superclass";
			}
		}
		public override string cmdName
		{
			get
			{
				return "MoveFunctionToSuperclass";
			}
		}
		public override string qualifiedName
		{
			get
			{
				return "RefactorAddIn.Connect.MoveFunctionToSuperclass";
			}
		}
		public override string shortDescription
		{
			get
			{
				return "Move item to super class";
			}
		}
		public override int iconId
		{
			get
			{
				return 54;
			}
		}
		public override int position
		{
			get
			{
				return 1;
			}
		}

		#endregion

		#region connect calls
		protected override bool doExec()
		{
			if (!isMoveableItem())
				return false;
			switch (element2Move.Kind)
			{
				case vsCMElement.vsCMElementFunction:
					copyFunction();
					break;
				case vsCMElement.vsCMElementProperty:
					copyProperty();
					break;
				case vsCMElement.vsCMElementVariable:
					copyVariable();
					break;
			}
			sourceClass.RemoveMember(element2Move);
			return true;
		}
		/// <summary>
		/// can we do this?
		/// </summary>
		/// <returns></returns>
		protected override EnvDTE.vsCommandStatus doQueryStatus()
		{
			identifySelectedItem();
			if (isMoveableItem())
			{
				// superclass must not have a class of this signature
				if(alreadyHasElement(targetClass,(CodeElement) element2Move))
					return vsCommandStatus.vsCommandStatusUnsupported;
				else
					return vsCommandStatus.vsCommandStatusEnabled | vsCommandStatus.vsCommandStatusSupported;
			}
			return vsCommandStatus.vsCommandStatusUnsupported;
		}
		#endregion

		#region worker stuff
		/// <summary>
		/// determine what it actually is that has been selected.
		/// sets element2Move and SourceClass
		/// </summary>
		private void identifySelectedItem()
		{
			element2Move = null;
			property2Move = (CodeProperty) this.selectedElement(vsCMElement.vsCMElementProperty);
			if (property2Move != null)
			{
				element2Move = (CodeElement)property2Move;
				sourceClass = property2Move.Parent;
			}
			function2Move = (CodeFunction) this.selectedElement(vsCMElement.vsCMElementFunction);
			if (function2Move != null)
			{
				element2Move = (CodeElement)function2Move;
				sourceClass = (CodeClass)function2Move.Parent;
			}
			Variable2Move = (CodeVariable) this.selectedElement(vsCMElement.vsCMElementVariable);
			if (Variable2Move != null)
			{
				element2Move = (CodeElement)Variable2Move;
				sourceClass = (CodeClass)Variable2Move.Parent;
			}
		}
		/// <summary>
		/// get the detail of the item to move and its target.
		/// sets Element2Move and targetClass
		/// </summary>
		/// <returns>true if circumstances allow moving it</returns>
		private bool isMoveableItem()
		{
			//condition something must be a selectied
			if (element2Move == null)
				return false;
			// condition 2: it must be part of a CodeClass
			if (sourceClass == null)
				return false;
			// condition 3: it must have a super class
			if (sourceClass.Bases.Count == 0)
				return false;
			for (int idx = 1; idx <= sourceClass.Bases.Count; idx++)
			{	
				CodeElement base1 = sourceClass.Bases.Item(idx);
				// the base must be a class, not an interface
				if (base1.Kind == vsCMElement.vsCMElementClass)
				{
					targetClass = base1 as CodeClass;
					return (IsPartOfProject(targetClass));
				}
			}
			return false;
		}
		/// <summary>
		/// check if a Class is part of the project or not
		/// </summary>
		/// <param name="parent"></param>
		/// <returns></returns>
		private bool IsPartOfProject(CodeClass parent)
		{		
			try 
			{
				ProjectItem project = parent.ProjectItem;
			}
			catch (Exception) {return false;} 
			return true;
		}

		/// <summary>
		/// identify if the parent classs already has anelement of this signature
		/// </summary>
		/// <param name="parent"></param>
		/// <param name="elementToMove"></param>
		/// <returns></returns>
		private bool alreadyHasElement(CodeClass parent, CodeElement elementToMove)
		{
			foreach (CodeElement member in parent.Members)
			{
				if ((member.Kind == elementToMove.Kind)
					&&(member.Name == elementToMove.Name))
				{
					switch (elementToMove.Kind)
					{
						case vsCMElement.vsCMElementFunction:
							return FunctionsHaveSameSignature((CodeFunction)elementToMove, (CodeFunction)member);
						case vsCMElement.vsCMElementProperty:
							return true;
						case vsCMElement.vsCMElementVariable:
							return true;						
						default:
							return true;
					}
				}
			}
			return false;
		}
		/// <summary>
		/// compare the signature of two function. This means checking the parameters
		/// for their types. The return value is not checked 
		/// </summary>
		/// <param name="func1"></param>
		/// <param name="func2"></param>
		/// <returns>true if the signatures are the same</returns>
		private bool FunctionsHaveSameSignature(CodeFunction func1, CodeFunction func2)
		{
			bool sameSoFar = (func1.Parameters.Count == func2.Parameters.Count);
			if (sameSoFar)
			{
				IEnumerator func1Params = func1.Parameters.GetEnumerator();
				IEnumerator func2Params = func2.Parameters.GetEnumerator();
				while (func1Params.MoveNext())
				{
					func2Params.MoveNext();
					CodeParameter func1Param = (CodeParameter)(func1Params.Current);
					CodeParameter func2Param = (CodeParameter)(func2Params.Current);
					if (func1Param.Type.TypeKind != func2Param.Type.TypeKind)
						return false;
				}
				return true;
			}
			else return false;
		}

		/// <summary>
		/// create a copy of the function in the targetclass
		/// </summary>
		private void copyFunction()
		{
			CodeFunction cf = (CodeFunction)element2Move;
			EditPoint src =cf.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes).CreateEditPoint();
			string text = src.GetText(cf.GetEndPoint(vsCMPart.vsCMPartWholeWithAttributes));

			string filename = targetClass.ProjectItem.get_FileNames(0);
			CodeFunction newCF = targetClass.AddFunction(
				element2Move.Name,vsCMFunction.vsCMFunctionFunction,cf.Type,-1,cf.Access, filename);
			TextPoint tp = newCF.GetStartPoint(EnvDTE.vsCMPart.vsCMPartWholeWithAttributes);
			EditPoint  ep =tp.CreateEditPoint();
			ep.ReplaceText(newCF.GetEndPoint(EnvDTE.vsCMPart.vsCMPartWholeWithAttributes),
				text,3);
			sourceClass.RemoveMember(element2Move);
		}

		private void copyVariable()
		{
			string filename = targetClass.ProjectItem.get_FileNames(0);
			CodeVariable newVar = targetClass.AddVariable(
				element2Move.Name, Variable2Move.Type , 1 ,Variable2Move.Access, filename);


			EditPoint src =Variable2Move.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes).CreateEditPoint();
			string text = src.GetText(Variable2Move.GetEndPoint(vsCMPart.vsCMPartWholeWithAttributes));

			TextPoint tp = newVar.GetStartPoint(EnvDTE.vsCMPart.vsCMPartWholeWithAttributes);
			EditPoint  ep =tp.CreateEditPoint();

			ep.ReplaceText(newVar.GetEndPoint(EnvDTE.vsCMPart.vsCMPartWholeWithAttributes),
				text,3);
			sourceClass.RemoveMember(element2Move);
		}

		private void copyProperty()
		{
			string filename = targetClass.ProjectItem.get_FileNames(0);
			CodeProperty newProperty = targetClass.AddProperty(
				"A", "A", property2Move.Type , 1 ,property2Move.Access,   filename);
			newProperty.Name = property2Move.Name;


			EditPoint src =property2Move.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes).CreateEditPoint();
			string text = src.GetText(property2Move.GetEndPoint(vsCMPart.vsCMPartWholeWithAttributes));

			TextPoint tp = newProperty.GetStartPoint(EnvDTE.vsCMPart.vsCMPartWholeWithAttributes);
			EditPoint  ep =tp.CreateEditPoint();

			ep.ReplaceText(newProperty.GetEndPoint(EnvDTE.vsCMPart.vsCMPartWholeWithAttributes),
				text,3);
			sourceClass.RemoveMember(element2Move);
		}

		#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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Australia Australia
I am a Software Engineer/Consultant. My work is focussed on helping teams to get more out of their work. So I teach how to do requirements, analysis and design in a format that is easy to understand and apply.
I help with testing too, from starting developers on automated unit testing to running whole testing teams and how they cooperate with development.

For really big projects I provide complete methodologies that support all of the lifecycle.

For relaxation I paddle a sea kayak around Sydney and the Central Coast or write utilities on rainy days to make my life easier.

Comments and Discussions