Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / Visual Basic

Abstract Method / Interface Implementation Macro

Rate me:
Please Sign up or sign in to vote.
2.71/5 (7 votes)
9 Jun 2003CPOL3 min read 85.6K   650   15   8
Macro for auto-implementing abstract methods extended by given class or interfaces.

Introduction

So... I got pretty sick and tired of re-overriding abstract methods for each class based off an abstract one, and decided to write the following macro so I wouldn't have to do it, ever again :).

Notes:

  1. Code should have better modularity, and will in the future. If you want to contribute, contact me --> yaron@valor.com.
  2. Uses ProjectItem, which means it requires that you select an item from the Class View and right-click on it to get the context menu, then ... well, you'll get the hang of it.
  3. Setup also auto-removes itself, but it's badly written. It does, however, get the job done.

Installing:

To install, download the vsmacros ZIP file (above), and extract it into any directory. Then go to Visual Studio .NET, and hit Alt-F8. The Macros window should open up. Select the topmost element ("Macros"), right-click and choose "Load Macro Project...", and then select the file from where you dropped it to. You will now get an element named CodeAssist under the "Macros" parent. Hit "Setup" to have it bind the popup-menus to your Solution and Class View. You're set!

Usage:

Select a class from your Solution or Class View, right-click and choose "Code-Assist -> Implement Abstract Methods". This will automatically add methods with their respective comments from parent abstract classes and interfaces.

Comments welcome, and encouraged!!!

The Code

First off, some functional tools:

  1. checkLanguage - Verifies that a given document is in C#. Otherwise throws exception.
    VB
    ' Checks to see that we're using
    ' CSharp (otherwise, this thing WILL fail)
    Private Sub checkLanguage(ByVal document As Document)
        If document.Language <> "CSharp" Then
            Throw New System.Exception("This Macro only works on C# code")
        End If
    End Sub
  2. hasFunction - Verifies if function exists in class (Note - this is still WIP, as it doesn't check overload for same name, different parameters). Will be improved in the future. To perform this, we received a ClassElement (of type CodeClass), and we then retrieve a list of its class members. We then iterate through them and find functions of the same name. Note that we check if the member is a function, by comparing against vsCMElementFunction.
    VB
    ' Checks to see if function already exists in class
    'TODO: Make this check parameters, as well
    Private Function hasFunction(ByVal classElement As CodeClass, _ 
                                ByVal functionElement As CodeFunction)
        Dim member As CodeElement
        For Each member In classElement.Members
            If (member.Kind = vsCMElement.vsCMElementFunction) Then
                If (member.Name = functionElement.Name) Then
                    Return True
                End If
            End If
        Next
    
        Return False
    End Function

Now we start with the nuts and bolts of the whole thing:

  1. processBaseClasses - this piece here will process base classes and interfaces, and add their abstract functions or must-implement methods to the the list of functions to be implemented. First off, it receives a functions ArrayList - note that this function is recursive, and therefore this is the sole container of all the functions which are to be processed later. It also receives the list of base classes and implemented interfaces which a given class contains. These are iterated through and each of their "MustImplement" functions are added to the container (functions), and will then be added to the class itself.

    The code contains comments, which will allow for following the code. I will expand on request.

    VB
    ' Recursively process base classes
    Sub processBaseClasses( _
        ByVal functions As ArrayList, _
        ByVal baseClasses As CodeElements, _
        ByVal implementedInterfaces As CodeElements)
    
        ' Create elements array
        Dim baseElement As CodeElement
    
        ' Collect all items
        Dim elements As ArrayList = New ArrayList()
        For Each baseElement In baseClasses
            elements.Add(baseElement)
        Next
        If (Not implementedInterfaces Is Nothing) Then
            For Each baseElement In implementedInterfaces
                elements.Add(baseElement)
            Next
        End If
    
        ' Exit if done
        If (elements.Count = 0) Then
            Exit Sub
        End If
    
        ' Process base classes
        Dim cFunction As CodeFunction
        For Each baseElement In elements
            Debug.WriteLine(baseElement.Name)
    
            If (baseElement.Kind = vsCMElement.vsCMElementInterface) Then
                ' Recurse
                processBaseClasses( _
                    functions, _
                    baseElement.Bases, _
                    Nothing _
                )
            Else
                ' Recurse
                processBaseClasses( _
                    functions, _
                    baseElement.Bases, _
                    baseElement.ImplementedInterfaces)
            End If
    
            ' Check if this level implements lower ones
            Dim fCnt As Integer = 0
            Do While fCnt < functions.Count
                cFunction = functions.Item(fCnt)
                If (baseElement.Kind = vsCMElement.vsCMElementClass) Then
                    If (hasFunction(baseElement, cFunction)) Then
                            functions.Remove(cFunction)
                    End If
                Else
                    fCnt += 1
                End If
            Loop
    
            ' Get members
            Dim member As CodeElement
            For Each member In baseElement.Members
                If (member.Kind = vsCMElement.vsCMElementFunction) Then
                    cFunction = member
    
                    If (cFunction.MustImplement) Then
                        functions.Add(cFunction)
                    End If
    
                End If
            Next
        Next
    End Sub
  2. This is the subroutine which is called when clicking the "Implement Abstract Methods" popup menu item:

    1. It will process the list of classes in the given file.
      1. Receives list of functions to be implemented (see processBaseClasses, above)
      2. Adds them to the current class.
      VB
      ' Impelements abstract methods for class
      Sub ImplementAbstractMethods()
          Try
              ' Life stinks...
              If (DTE.SelectedItems.Count <> 1) Then
                  MsgBox("Function will only work on 1 file")
                  Exit Sub
              End If
      
              ' Get current selection
              Dim currItem As ProjectItem = _
                DTE.SelectedItems.Item(1).ProjectItem
              If (Not currItem.IsOpen) Then
                  currItem.Open()
              End If
      
              Dim currDoc As Document = currItem.Document
      
              ' Check compatibility with language
              CheckLanguage(currDoc)
      
              ' Create arraylist to contain all abstracts items
              Dim functions As ArrayList = New ArrayList()
              Dim cFunction As CodeFunction
      
              ' Get namespace
              Dim fileCM As FileCodeModel = _ 
                currDoc.ProjectItem.FileCodeModel
              Dim cNameSpace As CodeNamespace = _ 
                fileCM.CodeElements.Item(1)
      
              ' Process classes
              Dim classElement As CodeClass
              For Each classElement In cNameSpace.Members
                  processBaseClasses( _
                      functions, _
                      classElement.Bases, _
                      classElement.ImplementedInterfaces _
                  )
      
                  ' Process all members
                  DTE.UndoContext.Open("Add Abstract Implementations")
      
                  For Each cFunction In functions
                      ' Check if exists in class
                      If (Not hasFunction(classElement, cFunction)) Then
                          ' Add function
                          Dim newFunction As CodeFunction
                          Dim type As String = cFunction.Type.AsString
                          If (cFunction.Parent.Kind = _ 
                            vsCMElement.vsCMElementFunction) Then
                              type = "override " + type
                          End If
      
                          ' Create function
                          newFunction = classElement.AddFunction( _
                              cFunction.Name, _
                              vsCMFunction.vsCMFunctionFunction, _
                              cFunction.Type.AsString, _
                              -1, _
                              cFunction.Access _
                          )
      
                          ' Create comment block
                          Dim commentString As String
                          commentString = _
                              "Implementation of " + _ 
                              cFunction.FullName
      
                          ' Add parameters
                          Dim parameter As CodeParameter
                          For Each parameter In cFunction.Parameters
                              newFunction.AddParameter( _
                                  parameter.Name, _
                                  parameter.Type.AsString() _
                              )
      
                              ' Add parameter comment
                              commentString += _
                                      vbCrLf + "<param name=""" _ 
                                      + parameter.Name + _ 
                                      """></param>"
                          Next
      
                          ' now add comment
                          newFunction.Comment = commentString
                      End If
                  Next
      
                  ' Close undo context
                  DTE.UndoContext.Close()
              Next
      
          Catch ex As System.Exception
      
              MsgBox(ex.ToString())
      
          End Try
      
      End Sub

Setup Script (run once)

Now this snippet here was quite a challenge to create. I had been searching around for several of the items here until finally I understood the way things worked. I broke it down into 3 main methods:

  1. GetCommandBars - Retrieves command bars for Solution and Class popups.
  2. AddMenu - Removes old submenu items (if exists), and adds new submenu.
  3. AddCommand - Adds given macro command to menu item.

Please note bolded line in AddMenu (addCommand) -> points to macro location. It's currently pointing to a project named VisualStudioMacros. If this doesn't make sense to you or you deposit the macros in a different project - please change this.

VB
Imports EnvDTE
Imports System.Diagnostics
Imports System.IO
Imports System.Text
Imports System.Collections
Imports Microsoft.Office.Core
VB
Public Module Setup
VB
Private Function getCommandBars() As ArrayList
    Dim barList As ArrayList = New ArrayList()

    ' Locate solution and class view popups
    Dim cmdBar As CommandBar
    For Each cmdBar In DTE.CommandBars
        Dim cmdBarCtl As CommandBarControl
        If ((cmdBar.Name = "Item") Or _
         (cmdBar.Name = "Class View Item")) Then
            barList.Add(cmdBar)
        End If
    Next

    ' Couldn't find bars
    If (barList.Count = 0) Then
        Throw New _
          System.Exception("Failed to locate Popup Menus!")
    End If

    ' return bars
    Return barList
End Function
' This should be run ONLY ONCE
Private Sub AddMenu()
    Dim menu As CommandBarButton
    Dim cmdBar As CommandBar
    Dim cmdBars As ArrayList
    Dim cmdBarCtl As CommandBarControl

    ' Get command bars
    cmdBars = getCommandBars()

    ' Remove all oldies
    For Each cmdBar In cmdBars
        Dim ctrlCnt As Integer = 1
        While (ctrlCnt <= cmdBar.Controls().Count)
            cmdBarCtl = cmdBar.Controls().Item(ctrlCnt)
            If (cmdBarCtl.Caption.IndexOf("Code-Assist") _
                                          > -1) Then
                cmdBarCtl.Delete()
            Else
                ctrlCnt += 1
            End If
        End While

        ' Create menu
        Dim menuPopup As CommandBarPopup
        menuPopup = _
         cmdBar.Controls.Add(vsCommandBarType.vsCommandBarTypePopup)
        menuPopup.BeginGroup = True
        menuPopup.Caption = "Code-Assist"

        ' Add commands
        addCommand( _
          "Implement Abstract Methods", _
          "Macros.VisualStudioMacros.Coding.ImplementAbstractMethods", _
          menuPopup _
        )
    Next
End Sub
VB
' Get command to add
Sub addCommand( _
    ByVal caption As String, _
    ByVal cmdName As String, _
    ByVal menuPopup As CommandBarPopup)

    Dim cmd As Command
    Dim cmdBarCtl As Microsoft.Office.Core.CommandBarControl

    ' Get item
    cmd = DTE.Commands.Item(cmdName)
    ' Add to command bar
    cmdBarCtl = cmd.AddControl(menuPopup.CommandBar())
    cmdBarCtl.Caption = "Implement Abstract Methods"
End Sub
VB
    ' Sets up menus, ...
    Sub Setup()
        AddMenu()

        MsgBox("Setup done!" & vbNewLine & _ 
           "Check Item menu for Code-Assist entries")
    End Sub

End Module

That's it!

History

  • Updated -> June 10th, 9:30 EST
    1. Added source code to article.
    2. As per request, now added ZIP file of macros project (.vsmacros).
  • Updated -> June 3rd, 9:30 EST
    1. Fixed Setup to perm-remove menu items instead of temporarily.
    2. Added menu item to both Class and Solution item views.

License

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


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

Comments and Discussions

 
Generaldownload Pin
Jan Vercauteren9-Jun-03 21:22
Jan Vercauteren9-Jun-03 21:22 
whrer's the source?
GeneralRe: download Pin
Yaron Golan9-Jun-03 21:46
Yaron Golan9-Jun-03 21:46 
GeneralRe: download Pin
Jan Vercauteren10-Jun-03 3:26
Jan Vercauteren10-Jun-03 3:26 
GeneralSome ideas Pin
Diego Mijelshon3-Jun-03 2:34
Diego Mijelshon3-Jun-03 2:34 
GeneralVs.NET 2003 Pin
Marc Clifton2-Jun-03 6:22
mvaMarc Clifton2-Jun-03 6:22 
GeneralRe: Vs.NET 2003 Pin
James T. Johnson2-Jun-03 8:59
James T. Johnson2-Jun-03 8:59 
GeneralRe: Vs.NET 2003 Pin
Yaron Golan2-Jun-03 20:37
Yaron Golan2-Jun-03 20:37 
GeneralRe: Vs.NET 2003 Pin
James T. Johnson2-Jun-03 20:45
James T. Johnson2-Jun-03 20:45 

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.