Click here to Skip to main content
15,883,705 members
Articles / Programming Languages / C#

FindReferences of "N Order" - SharpDevelop Add-In

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
1 Mar 2009CPOL5 min read 27.4K   477   23   2
Using ICSharpCode.SharpDevelop.Refactoring.RefactoringService.FindReferences to find all references in a given project

Introduction "What is the Problem?"

Using Visual Studio, you can easily right-click on a method and choose "FindAllReferences" to list all methods that call your method as in the following image. You can only see what I call "First Order Reference" which are methods that directly call your method. In many situations, you need "N Order Reference", i.e. you need to know that caller of caller methods, ...etc.

VS.JPG

In very large projects, you need a deeper view. It is not always enough to know that MethodC calls MethodA. You need to know if there is a relation between MethodZ & MethodA through a series of calls, e.g. Z->y->X-> ......... C->B->A ?! It is important to know this information to decide whether you need to re-test MethodZ or not.

Idea & Implementation "How Can We Solve It?"

Our utility "Reference Analysis" is a SharpDevelop Add-In that is installed on SharpDevelop 3.0. It uses SharpDevelop library to walk over all methods in all classes of all projects in an open solution and get all direct caller methods "First Order Caller". This information is stored in XML file using the following schema:.  

Menu.JPG

As we can see, there are three main tables, Projects - Classes & Methods. There is a one-to-many relation that exists between Project and Classes. Another one-to-many relation exists between Classes & Methods. Finally there is a many-to-many relationship between Methods and themselves.

If we navigate on all methods in a solution and store callers using the above schema, we can easily query to get list of "N Order Callers". 

Using the Code

The code has three main parts:

Addon Part

This is the code that enables SharpDevelop to recognize the utility as a Add-In module.

Menu.JPG

A SharpDevelop Add-in can consist of a zip file with two files inside; DLL file & Addin file. The DLL contains all code & logic, while the .addin file is an XML based format file that defines attributes of the plug-in to SharpDevelop IDE. Our Reference Analysis addin is a zip file of FindReferences.addin & FindReferences.dll. The FindReferences.addin file contains information about the addin such as:

XML
 <addin name="""ReferenceList""" author="""Mohammad"" 
	url="""""" description="""TODO:"">
	<path name="""/Workspace/Tools""">
	<menuitem class="""Addin1.ReferenceListToolCommand""" 
	id="""Reference"" label="""Reference"">
	<addin name="""ReferenceList""" author="""Mohammad"" 
	url="""""" description="""TODO:""><path name="""/Workspace/Tools""">
	<menuitem class="""Addin1.ReferenceListToolCommand""" 
	id="""Reference"" label="""Reference""><AddIn name = "ReferenceList" 
          author = "Mohammad Said Hefny" 
          url = "" 
          description = "TODO: List all callers of a given project. 
			can be used in an impact analysis."> 
  <Runtime> 
          <Import assembly = "FindReferences.dll"> </Import> 
  </Runtime> 

  <Manifest> 
          <Identity name="FindReferences"/> 
          <Dependency addin="SharpDevelop" version="3.0"/> 
  </Manifest> 

  <Path name = "/Workspace/Tools"> 
          <MenuItem id = "Reference Analysis" label = "Reference Analysis" 
		class = "SharpDevelopAddin.ReferenceListToolCommand" /> 
  </Path> 
</AddIn> 

The file provides description and points to class SharpDevelopAddin.ReferenceListToolCommand where SharpDevelop expects to find a Method called Run() to call. In the Run() method, you can either use SharpDevelop GUI control that as View Control or you can use normal dialog controls as the one used for this utility. Please refer to "Line Counter - Writing a SharpDevelop Add-In" for more detailed information about how to make an Add-in.

Code Analysis Part

This is the core of this tool. It is where all data is extracted. Generated data can be saved as XML for future use.

First the tool extracts the project from the solution to enable the user to select only projects he/she needs to analyze.

C#
// Retrieve Currently Open Solution.
ICSharpCode.SharpDevelop.Project.Solution Sol = 
	ICSharpCode.SharpDevelop.Project.ProjectService.OpenSolution;
        
// Enumerate on Projects
foreach (ICSharpCode.SharpDevelop.Project.IProject iProj in Sol.Projects) 
{
    System.Windows.Forms.ListViewItem lstProjectItem = 
		new System.Windows.Forms.ListViewItem(); 
          
    // Extract Project Display Data in a ListView Item
    lstProjectItem.Text = iProj.Name;
    lstProjectItem.Tag = iProj;
    lstProjectItem.SubItems.Add(iProj.Language);
    lstProjectItem.SubItems.Add(iProj.AssemblyName);blyName);
        
    // Add ListViewItem to ListView
    lstProjects.Items.Add        }      

Property ICSharpCode.SharpDevelop.Project.ProjectService.OpenSolution returns the current open solution. It contains a list of ICSharpCode.SharpDevelop.Project.IProject that we enumerate on them to retrieve projects of the solution. Then we will a listview of these projects to enable the user to select them for analysis.

After the user selects the projects as in the image below:

Select_Project.JPG

The project starts analyzing each method in these projects and traces the calls into all projects in the solution. The following code explains this technically:

First we get enumerate on selected projects that the user selected in the last image. A call to ICSharpCode.SharpDevelop.ParserService.GetProjectContent(iProj) returns all information for a given project. The information is that we need now is classes of the project ProjContent.Classes. This property returns a list of ICSharpCode.SharpDevelop.Dom.IClass. Now we enumerate on classes to retrieve Methods by using property iClass.Methods that returns a list of ICSharpCode.SharpDevelop.Dom.IMethod.

Now we can get all Methods in a given solution. The second important part is to FindReference these methods. This is done by calling ICSharpCode.SharpDevelop.Refactoring.RefactoringService.FindReferences(Im, null). This function takes IMethod as a parameter and returns a list of all methods that call this function even if these methods are in other projects.

The following code explains the above approach:

C#
// For Each Selected Project Get All Classes
foreach ( ICSharpCode.SharpDevelop.Project.IProject iProj   in mSelectedProjects)
{
    // Now Get Project Contents
    ICSharpCode.SharpDevelop.Dom.IProjectContent ProjContent = 
    	ICSharpCode.SharpDevelop.ParserService.GetProjectContent(iProj);
  
    if (ProjContent != null)
    {
        // Look For Classes
        foreach (ICSharpCode.SharpDevelop.Dom.IClass iClass in ProjContent.Classes)
        {
            //Get Classes Contents 
            Src.Gui.Dialogs.ReferenceDialog.DSReference.ClassRow ClassRow = 
            AddClassRow(iClass.Name, iClass.ProjectContent.Project.ToString () );

            // Enumerate All Methods in the class
            foreach (ICSharpCode.SharpDevelop.Dom.IMethod iMethod in iClass.Methods)
            {
                ICSharpCode.SharpDevelop.Dom.IMember Im = 
                	(ICSharpCode.SharpDevelop.Dom.IMember)iMethod;

                // Call ICSharpCode.SharpDevelop.Refactoring.
                // RefactoringService.FindReferences to Find References.
                List<ICSharpCode.SharpDevelop.Refactoring.Reference&gt Reff = 
                	ICSharpCode.SharpDevelop.Refactoring.
		RefactoringService.FindReferences(Im, null);

                // Save Result in XML
                Src.Gui.Dialogs.ReferenceDialog.DSReference.MethodRow MethodRowCalled = 
                	AddMethodRow(iMethod.Name, ClassRow);

                string CalledMethodFullName = iClass.Name + ":" + iMethod.Name;
                foreach (ICSharpCode.SharpDevelop.Refactoring.Reference Ref in Reff)
                {
                    AddMethodRelationRow(MethodRowCalled, Ref.ResolveResult );
                }
            } 
        }
    }

Display Part

This module has a standalone version that uses XML saved data and displays it.

ResultSample.JPG

Although the tool works fast for small projects, generating call reference method of all Methods in a large project can take a very long time. We are talking here about many hous or even couple of days!! This is a very time consuming task, so to avoid running this task very often, the results can be stored in XML format and can be retrieved later or distributed on different developers who are interested in seeing the result. The XML structure represents a simple database with three tables References - Classes - Methods. You can work on that XML to extract more information if you need.

How to Make It Work

  1. Installing & Running Reference Analysis Add-In:

    Menu.JPG

    • Download ReferenceAnalysis.zip
    • Open SharpDevelop, goto Tools\AddinManager
    • Select the zip file as an addin and restart SharpDev.
    • Now download SampleClasses.zip and extract it. And open the solution in SharpDevelop.
    • Goto "Tools\Reference Analysis" and click it.
    • Select FindReference and you will see only one project reference that you need to click on to select.
    • Results will be displayed in the main dialog and you can save the results in XML file.
  2. Running Reference Analysis Desktop Version:

    • Download Desktop.zip
    • Run DeskTopTool.exe
    • Press Open File & Select "SampleResult.xml" found in the zip file.
    • Now you can see the result sample.

Why is this Data Useful?

Well, the data retrieved from this utility gives a complete view of "what calls what" in a given solution. This can be used in an impact analysis study of a code refactoring. The utility will allow you to tell which methods, classes & projects are affected or should be re-tested as a result of refactoring a given method.

Extra Enhancements

You can extend this project to draw reference dependency diagram or a class dependency diagram represents which classes are called by which classes.You can extract more information such as the weight of each method depending on the number or callers. If you need to enhance a code response, this data will help you to select which method you should enhance first and which methods will have impact when changed.

History

  • 1st March, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


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

Comments and Discussions

 
GeneralVery Interesting - But problem in FindReferences.zip Pin
MarkusD1-Mar-09 20:41
MarkusD1-Mar-09 20:41 
GeneralRe: Very Interesting - But problem in FindReferences.zip Pin
Mohammad Said Hefny2-Mar-09 0:01
Mohammad Said Hefny2-Mar-09 0:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.