Generating from source code

CodeSmith Generator can generate from any kind of metadata, including existing source code. The CodeSmith.CodeFileParser class parses a string or a file and returns an easy-to-use object model of the code.

Requirements

The string or file you parse must contain valid C# or Visual Basic code. CodeFileParser uses the open source NRefactory library to build its object model.

The CodeFileParser object

You can create a CodeFileParser in code with one of its constructors:

// There are overloads that don't require basePath or parseMethodBodies.
public CodeFileParser(string fileName, string basePath, bool parseMethodBodies)

// There are overloads that don't require parseMethodBodies.
public CodeFileParser(string source, SupportedLanguage language, bool parseMethodBodies)

You can also make it a template property: add a Property directive whose type is CodeSmith.CodeFileParser. The user then picks a C# or Visual Basic file in the property grid.

Selection methods

Most NRefactory methods return positions as Location objects. They're descriptive, but not convenient when you want to take substrings or selections of the existing code, which is often what you need for code generation. CodeFileParser adds methods that take Location objects and return strings:

public string GetSectionFromStart(Location end)
public string GetSectionToEnd(Location start)
public string GetSection(Location start, Location end)

CodeDomCompilationUnit

To walk the code quickly, use the CodeDomCompilationUnit property. It's loaded on first use and returns a standard .NET System.CodeDom.CodeCompileUnit that contains the complete code graph. This is the quickest and easiest way to traverse your metadata.

Visitors

When you need more advanced or customized information, use the CompilationUnit property. It accepts a visitor object that traverses the syntax tree and collects the data you want. This is an NRefactory feature: your visitor class derives from AbstractAstVisitor.

Example

This template lists the namespaces, types and properties in the file the user picks:

<%@ CodeTemplate Language="C#" TargetLanguage="Text" Debug="False" %>
<%@ Property Category="2.Class" Name="TheFile" Type="CodeFileParser" Optional="False" %>
<%@ Assembly Name="CodeSmith.CodeParser" %>
<%@ Import Namespace="System.CodeDom" %>

<% foreach(CodeNamespace n in TheFile.CodeDomCompilationUnit.Namespaces) { %>
    Namespace: <%= n.Name %>
    <% foreach(CodeTypeDeclaration t in n.Types) { %>
        Type: <%= t.Name %>
        <% foreach(CodeTypeMember member in t.Members) {
               CodeMemberProperty m = member as CodeMemberProperty;
               if (m == null) continue; %>
            Property Name: <%= m.Name %> Property Type <%= m.Type.BaseType %>
        <% } %>
    <% } %>
<% } %>

Where Generator uses CodeFileParser

  • The InsertClass merge strategy parses the existing code file to find where to insert new content.
  • PLINQO for LINQ to SQL uses it to merge metadata classes: it maps all the properties in the class so that their attributes are preserved when you regenerate.