Prime Programming Proficiency, Part 3: Lines-of-code Counter
Listing 6 is the SolutionElement. It implements IHost and is initialized with a Projects collection when the constructor—Sub New—is called. The Accept method implements IHost and accepts a visitor. The Iterate method does the interesting work. Iterate clears the Output device (see the Implementing the Output Window section later in this article), writes the name of the current solution, and then iterates over each Project in the Projects collection.
Notice that I create only one instance of ProjectElement and reuse it inside the For Each loop. Because it is a wrapper and its state is transient, this conservative code will reduce memory chunking. Within the loop, the ProjectElement is told which ProjectItem it currently represents and then the ProjectElement is visited.
Listing 6: The ProjectElement.
Imports EnvDTE Imports System Imports System.Diagnostics Public Class ProjectElement Implements IHost Private FProject As Project Private FVisitor As IVisitor Public Sub New() End Sub Public Sub New(ByVal Project As Project) FProject = Project End Sub Public Sub Accept(ByVal visitor As IVisitor) _ Implements IHost.Accept FVisitor = visitor visitor.Visit(Me) End Sub Public Property CurrentProject() As Project Get Return FProject End Get Set(ByVal Value As Project) FProject = Value End Set End Property Public Sub Iterate() Debug.Assert(FProject Is Nothing = False) Try Output.WriteLine("Project: " & FProject.Name) Catch Output.WriteLine("Project: <no name>") End Try If (FProject.ProjectItems Is Nothing) Then Return FVisitor.ProjectCount += 1 Dim item As ProjectItem Dim itemElement As ItemElement = New ItemElement For Each item In FProject.ProjectItems itemElement.CurrentItem = item itemElement.Accept(FVisitor) Next End Sub End Class
The ProjectElement represents an extensibility object model Project. Again, we implement IHost and call Accept to visit our host. The ProjectElement host also implements Iterate. At this level of granularity, we have more work to do. We output the project name, increment the visitor's ProjectCount, and then examine each ProjectItem using the ItemElement wrapper.
Listing 7: The ItemElement.
Imports EnvDTE Imports System Imports System.Diagnostics Public Class ItemElement Implements IHost Private FProjectItem As ProjectItem Private FVisitor As IVisitor Public Sub New() End Sub Public Sub New(ByVal item As ProjectItem) FProjectItem = item End Sub Public Property CurrentItem() As ProjectItem Get Return FProjectItem End Get Set(ByVal Value As ProjectItem) FProjectItem = Value End Set End Property Public Sub Accept(ByVal visitor As IVisitor) _ Implements IHost.Accept FVisitor = visitor visitor.Visit(Me) End Sub Public Sub Iterate(Optional ByVal Indent As String = " ") Iterate(FProjectItem, Indent) End Sub Private Sub Iterate(ByVal item As ProjectItem, _ Optional ByVal Indent As String = " ") Try Output.Write(Indent & "Name: " & item.Name) UpdateLineCount(item) FVisitor.ItemCount += 1 If (FProjectItem.ProjectItems Is Nothing = False _ Or FProjectItem.ProjectItems.Count > 0) Then Dim child As ProjectItem For Each child In item.ProjectItems Iterate(child, New String(" ", Indent.Length + 2)) Next End If Catch e As Exception Debug.WriteLine(e.Message) End Try End Sub Private Function GetLineCount(ByVal item As ProjectItem) ' "{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}" If (item.Kind = EnvDTE.Constants.vsProjectItemKindPhysicalFile) _ Then Return LineCounter.GetLineCount(item) Else Return 0 End If End Function Private Sub UpdateLineCount(ByVal item As ProjectItem) Try Dim count As Long = GetLineCount(item) Output.WriteLine(String.Format("({0})", count)) FVisitor.LineCount += count Catch Output.WriteLine("(0)") End Try End Sub End Class
ItemElement performs the most work. Again, it is a host with an Accept method and we want to iterate the ItemElement. Keep in mind that projects may have folders (sub-projects), so we have to look out for nested project items.
Iterate writes the ProjectItem name and updates the line count to include the ProjectItem contained in the ItemElement wrapper. Next, we update the visitor's ItemCount. After we have performed the information-gathering steps, we need to see whether this element has sub-elements. If the ProjectItem has sub-projects, we use recursion to examine those elements.
Two more methods are important to note: GetLineCount and UpdateLineCount. UpdateLineCount calls GetLineCount to get the actual number of lines, output that result, and add the count to the visitor's tally. GetLineCount uses the LineCounter class I implemented, which I cover in the next section.
Page 4 of 6
This article was originally published on August 12, 2004