Click here to Skip to main content
15,886,362 members
Articles / Desktop Programming / Windows Forms

Project Metadata Generation using T4

Rate me:
Please Sign up or sign in to vote.
4.97/5 (45 votes)
7 Nov 2009BSD16 min read 168.1K   1.5K   152  
Generate project metadata with T4 and unburden yourself from string literals in XAML binding expressions and INotifyPropertyChanged event arguments.
<#@ output extension=".vb" #>
<# ' For Silverlight support.  
#>
<#@ assembly name="C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll" #>

<#@ template language="VBv3.5" hostSpecific="true" #>
<#@ assembly name="System.dll" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="EnvDTE" #>
<#@ Import Namespace="EnvDTE" #>
<#@ Import Namespace="System" #>
<#@ Import Namespace="System.Text" #>
<#@ Import Namespace="System.Collections.Generic" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
' This code was automatically generated by Daniel Vaughan's metadata generator.
' Changes to this file may be lost if regeneration occurs.
' http://danielvaughan.orpius.com

<#
	If supportXamlBinding Then
		WriteLine("Imports System.Windows")
	End If
#>
Imports System
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Reflection

<# If supportObfuscation Then #>
Namespace DanielVaughan.Metadata	
	Friend Class ObfuscatedNameResolver
		' Methods
#If SILVERLIGHT <> ""
		Public Shared Function GetObfuscatedName(ByVal preobfuscationName As String, ByVal action As Func(Of String, String)) As String
			Dim result As String
			ObfuscatedNameResolver.membersLock.EnterUpgradeableReadLock
			Try 
				If Not ObfuscatedNameResolver.obfuscatedNames.TryGetValue(preobfuscationName, result) Then
					ObfuscatedNameResolver.membersLock.EnterWriteLock
					Try 
						If Not ObfuscatedNameResolver.obfuscatedNames.TryGetValue(preobfuscationName, result) Then
							result = action.Invoke(preobfuscationName)
							ObfuscatedNameResolver.obfuscatedNames.Item(preobfuscationName) = result
						End If
					Finally
						ObfuscatedNameResolver.membersLock.ExitWriteLock
					End Try
				End If
			Finally
				ObfuscatedNameResolver.membersLock.ExitUpgradeableReadLock
			End Try
			Return result
		End Function
	
		' Fields
		Private Shared ReadOnly membersLock As ReaderWriterLockSlim = New ReaderWriterLockSlim
		
#Else
		Public Shared Function GetObfuscatedName(ByVal preobfuscationName As String, ByVal action As Func(Of String, String)) As String
			Dim result As String
			If Not ObfuscatedNameResolver.obfuscatedNames.TryGetValue(preobfuscationName, result) Then
				SyncLock ObfuscatedNameResolver.membersLock
					If Not ObfuscatedNameResolver.obfuscatedNames.TryGetValue(preobfuscationName, result) Then
						result = action.Invoke(preobfuscationName)
						ObfuscatedNameResolver.obfuscatedNames.Item(preobfuscationName) = result
					End If
				End SyncLock
			End If
			Return result
		End Function
	
		' Fields
		Private Shared ReadOnly membersLock As Object = New Object
	#End If
		Private Shared ReadOnly obfuscatedNames As Dictionary(Of String, String) = New Dictionary(Of String, String)
	End Class
End Namespace
<# End If #>

<#
	Dim hostServiceProvider As IServiceProvider = Host
	Dim dte As DTE = DirectCast(hostServiceProvider.GetService(GetType(DTE)), DTE)
	Dim project As Project = dte.Solution.FindProjectItem(Host.TemplateFile).ContainingProject
	Dim namespaceBuilders As New Dictionary(Of String, NamespaceBuilder)
	Dim projectItem As ProjectItem
	For Each projectItem In project.ProjectItems
		Me.ProcessProjectItem(projectItem, namespaceBuilders)
	Next
	Dim item As Object
	For Each item In namespaceBuilders.Values
		Me.WriteLine(item.ToString)
	Next
#>

<#+
    ' Settings
	
	' The modifier to use when outputting classes.
    Private Const generatedClassAccessModifier As String = "Friend"
	
	' The prefix to use for output class and interface names. 
	' The combination of this and <see cref="generatedClassSuffix"/> provides 
	' MetaGen with the ability to identify those classes etc., 
	' for which it should generated metadata, and to ignore MetaGen generated classes.
    Private Const generatedClassPrefix As String = ""
	
	' The suffix to use for output class and interface names. 
	' The combination of this and <see cref="generatedClassSuffix"/> provides 
	' MetaGen with the ability to identify those classes etc., 
	' for which it should generated metadata, and to ignore MetaGen generated classes.
    Private Const generatedClassSuffix As String = "Metadata"
	
	' The child namespace in which to place generated items.
	' If there is a class in MyNamespace namespace, 
	' the metadata class will be generated
	' in the MyNamespace.[generatedClassSuffix] namespace. 
	' This string can be null or empty, in which case a subnamesapce 
	' will not be created, and generated output will reside 
	' in the original classes namespace.
    Private Const generatedNamespace As String = "Metadata"
	
    
	' Obfuscation is not yet supported for VB.NET
    Private Const supportObfuscation As Boolean = False
	' If <c>true</c> <see cref="System.Windows.PropertyPath"/> properties
	' will be generated for use in binding path assignment.
    Private Const supportXamlBinding As Boolean = True
	
	' The number of spaces to insert for a one step indent.
    Private Const tabSize As Integer = 4
	
	Public Sub ProcessProjectItem(ByVal projectItem As ProjectItem, ByVal namespaceBuilders As Dictionary(Of String, NamespaceBuilder))
        Dim fileCodeModel As FileCodeModel = projectItem.FileCodeModel
        If (Not fileCodeModel Is Nothing) Then
            Dim codeElement As CodeElement
            For Each codeElement In fileCodeModel.CodeElements
                Me.WalkElements(codeElement, Nothing, Nothing, namespaceBuilders)
            Next
        End If
        If (Not projectItem.ProjectItems Is Nothing) Then
            Dim childItem As ProjectItem
            For Each childItem In projectItem.ProjectItems
                Me.ProcessProjectItem(childItem, namespaceBuilders)
            Next
        End If
    End Sub

    Public Sub WalkElements(ByVal codeElement As CodeElement, ByVal parent As CodeElement, ByVal parentContainer As BuilderBase, ByVal namespaceBuilders As Dictionary(Of String, NamespaceBuilder))
        Dim codeElements As CodeElements
        Dim builder As NamespaceBuilder
        Dim name As String
        Dim comments As List(Of String)
		
        Me.indent += 1
		
        If (parentContainer Is Nothing) Then
            name = "global"
            If Not namespaceBuilders.TryGetValue(name, builder) Then
                builder = New NamespaceBuilder(name, Nothing, 0)
                namespaceBuilders.Item(name) = builder
            End If
            parentContainer = builder
        End If
	
        Select Case codeElement.Kind
			Case vsCMElement.vsCMElementNamespace
                Dim codeNamespace As CodeNamespace = DirectCast(codeElement, CodeNamespace)
                name = codeNamespace.FullName
                If Not String.IsNullOrEmpty(generatedNamespace) AndAlso name.EndsWith(generatedNamespace) Then
					Exit Select
				End If
			
				Dim tempBuilder As NamespaceBuilder
                If Not namespaceBuilders.TryGetValue(name, tempBuilder) Then
                    tempBuilder = New NamespaceBuilder(name, Nothing, 0)
                    namespaceBuilders.Item(name) = tempBuilder
                End If
			
				codeElements = codeNamespace.Members
				Dim element As CodeElement
				For Each element In codeElements
					Me.WalkElements(element, codeElement, tempBuilder, namespaceBuilders)
				Next
				Exit Select
            Case vsCMElement.vsCMElementClass
                Dim codeClass As CodeClass = DirectCast(codeElement, CodeClass)
                name = codeClass.Name
                If Not String.IsNullOrEmpty(generatedNamespace) _
					AndAlso codeClass.FullName.EndsWith(generatedNamespace) _
					OrElse (name.StartsWith(generatedClassPrefix) _
						AndAlso name.EndsWith(generatedClassSuffix)) Then
					Exit Select
				End If
				If supportObfuscation AndAlso codeClass.Access = vsCMAccess.vsCMAccessPrivate Then
					Exit Select
				End If

				Dim tempBuilder As BuilderBase
				comments = New List(Of String)
				comments.Add(String.Format("''' <summary>Metadata for class <see cref=""T:{0}""/></summary>", codeClass.FullName))
				If Not parentContainer.Children.TryGetValue(name, tempBuilder) Then
					tempBuilder = New ClassBuilder(name, comments, Me.indent)
					parentContainer.Children.Item(name) = tempBuilder
				End If
				codeElements = codeClass.Members
				If Not codeElements Is Nothing Then
					Dim ce As CodeElement
					For Each ce In codeElements
						Me.WalkElements(ce, codeElement, tempBuilder, namespaceBuilders)
					Next
				End If

            	Exit Select
		    Case vsCMElement.vsCMElementInterface
                Dim codeInterface As CodeInterface = DirectCast(codeElement, CodeInterface)
                name = codeInterface.Name
                If (Not name.StartsWith("") OrElse Not name.EndsWith("Metadata")) Then
                    comments = New List(Of String)
                    Dim commentName As String = FormatTypeNameForComment(codeInterface.FullName)
                    comments.Add(String.Format("''' <summary>Metadata for interface <see cref=""T:{0}""/></summary>", commentName))
                    Dim b As New InterfaceBuilder(name, comments, Me.indent)
                    parentContainer.AddChild(b)
                    codeElements = codeInterface.Members
                    If (Not codeElements Is Nothing) Then
                        Dim ce As CodeElement
                        For Each ce In codeElements
                            Me.WalkElements(ce, codeElement, b, namespaceBuilders)
                        Next
                    End If
                    Exit Select
                End If
                Exit Select
            Case vsCMElement.vsCMElementFunction
                Dim codeFunction As CodeFunction = DirectCast(codeElement, CodeFunction)
                If (codeFunction.Name <> parentContainer.Name _
					AndAlso codeFunction.Name <> "ToString" _
					AndAlso codeFunction.Name <> "Equals" _
					AndAlso codeFunction.Name <> "GetHashCode" _
					AndAlso codeFunction.Name <> "GetType" _
					AndAlso codeFunction.Name <> "MemberwiseClone" _
					AndAlso codeFunction.Name <> "ReferenceEquals" _
					AndAlso codeFunction.Name.StartsWith("~")) Then
                   	DirectCast(parentContainer, ClassBuilder).AddMember(codeFunction)
				End If                                
                Exit Select
            Case vsCMElement.vsCMElementVariable
                Dim codeVariable As CodeVariable = DirectCast(codeElement, CodeVariable)
                Dim parentBuilder As ClassBuilder = DirectCast(parentContainer, ClassBuilder)
                parentBuilder.AddMember(codeVariable)
                Exit Select
            Case vsCMElement.vsCMElementProperty
                Dim codeProperty As CodeProperty = DirectCast(codeElement, CodeProperty)
                If (codeProperty.Name <> "this") Then
                    DirectCast(parentContainer, ClassBuilder).AddMember(codeProperty)
                End If
                Exit Select
        End Select
        Me.indent -= 1
    End Sub

    Private Shared Function FormatTypeNameForComment(ByVal typeName As String) As String
        Return typeName.Replace("<"c, "{"c).Replace(">"c, "}"c)
    End Function

    Private Shared Function IsStatic(ByVal codeElement As CodeProperty) As Boolean
        If (Not codeElement.Getter Is Nothing) Then
            Return codeElement.Getter.IsShared
        End If
        Return ((Not codeElement.Setter Is Nothing) AndAlso codeElement.Setter.IsShared)
    End Function

    ' Nested Types
	
	Private indent As Integer
	
    Public MustInherit Class BuilderBase
        ' Methods
        Protected Sub New(ByVal name As String, ByVal comments As List(Of String))
            _name = name
            _comments = comments
        End Sub

        Public Overridable Sub AddChild(ByVal obj As BuilderBase)
            If Not _children.ContainsKey(obj.Name) Then
                _children.Add(obj.Name, obj)
            End If
        End Sub

        Public Overrides Function GetHashCode() As Integer
			If _name Is Nothing Then
				Return 0
			Else
				Return _name.GetHashCode
			End If
        End Function


        ' Properties
        Public ReadOnly Property Children As Dictionary(Of String, BuilderBase)
            Get
                Return _children
            End Get
        End Property

        Public ReadOnly Property Comments As List(Of String)
            Get
                Return _comments
            End Get
        End Property

        Public Property Indent As Integer
            Get
                Return _indent
            End Get
            Set(ByVal value As Integer)
                _indent = value
            End Set
        End Property

        Public ReadOnly Property Name As String
            Get
                Return _name
            End Get
        End Property


        ' Fields
        Private _children As Dictionary(Of String, BuilderBase) = New Dictionary(Of String, BuilderBase)
        Private _comments As List(Of String)
        Private _indent As Integer
        Private _name As String
    End Class

    Public NotInheritable Class NamespaceBuilder
        Inherits BuilderBase
        ' Methods
        Public Sub New(ByVal name As String, ByVal comments As List(Of String), ByVal indent As Integer)
            MyBase.New(name, comments)
        End Sub

        Public Overrides Function ToString() As String
            Dim indentString As String = String.Empty.PadLeft(MyBase.Indent)
            Dim sb As New StringBuilder
            Dim isGlobal As Boolean = (MyBase.Name = "global")
            If Not isGlobal Then
                sb.Append("Namespace ")
                sb.Append(MyBase.Name)
                If Not String.IsNullOrEmpty("Metadata") Then
                    sb.Append("."c)
                    sb.AppendLine("Metadata")
                Else
                    sb.AppendLine
                End If
            End If
            Dim item As BuilderBase
            For Each item In MyBase.Children.Values
                item.Indent = (MyBase.Indent + 4)
                sb.AppendLine(item.ToString)
            Next
            If Not isGlobal Then
                sb.AppendLine("End Namespace")
            End If
            Return sb.ToString
        End Function

    End Class

    Private Class ClassBuilder
        Inherits BuilderBase
        ' Methods
        Public Sub New(ByVal name As String, ByVal comments As List(Of String), ByVal indent As Integer)
            MyBase.New(name, comments)
            Me.propertyPaths = New Dictionary(Of String, PropertyPathBuilder)
        End Sub

        Public Sub AddMember(ByVal codeMember As CodeFunction)
            Dim memberName As String = codeMember.Name
            If Not MyBase.Children.ContainsKey(memberName) Then
                Dim formattedTypeName As String = FormatTypeNameForComment(codeMember.FullName)
                Dim comment As String = String.Format("''' <summary>Refers to method <see cref=""M:{0}""/></summary>", formattedTypeName)
                Dim comments As New List(Of String) 
				comments.Add(comment)
                Dim builder As New MethodBuilder(codeMember, comments)
                MyBase.Children.Add(memberName, builder)
            End If
        End Sub

        Public Sub AddMember(ByVal codeMember As CodeProperty)
            Dim memberName As String = codeMember.Name
            If Not MyBase.Children.ContainsKey(memberName) Then
                Dim formattedTypeName As String = FormatTypeNameForComment(codeMember.FullName)
                Dim comment As String = String.Format("''' <summary>Refers to property <see cref=""M:{0}""/></summary>", formattedTypeName)
                Dim comments As New List(Of String) 
				comments.Add(comment)
                Dim builder As New PropertyBuilder(codeMember, comments)
                MyBase.Children.Add(memberName, builder)
                Dim pathBuilder As New PropertyPathBuilder(codeMember, comments)
                Me.propertyPaths.Add(memberName, pathBuilder)
            End If
        End Sub

        Public Sub AddMember(ByVal codeMember As CodeVariable)
            Dim memberName As String = codeMember.Name
            If Not MyBase.Children.ContainsKey(memberName) Then
                Dim formattedTypeName As String = FormatTypeNameForComment(codeMember.FullName)
                Dim comment As String = String.Format("''' <summary>Refers to field <see cref=""M:{0}""/></summary>", formattedTypeName)
                Dim comments As New List(Of String)
				comments.Add(comment)
                Dim builder As New FieldBuilder(codeMember, comments)
                MyBase.Children.Add(memberName, builder)
            End If
        End Sub

        Public Overrides Function ToString() As String
            Dim indent1 As String = String.Empty.PadLeft(MyBase.Indent)
            Dim indent2 As String = String.Empty.PadLeft((MyBase.Indent + 4))
            Dim sb As New StringBuilder
            Dim line As String
            For Each line In MyBase.Comments
                sb.Append(indent1)
                sb.AppendLine(line)
            Next
            sb.Append(indent1)
            sb.Append("Friend")
            sb.Append(" Class ")
            sb.Append("")
            sb.Append(MyBase.Name)
            sb.AppendLine("Metadata")
            sb.Append(indent2)
            sb.AppendLine("Public Class MemberNames")
            Dim item As BuilderBase
            For Each item In MyBase.Children.Values
                item.Indent = (MyBase.Indent + 8)
                sb.Append(item.ToString)
                sb.AppendLine
            Next
            sb.Append(indent2)
            sb.AppendLine("End Class")
            sb.AppendLine
            Dim item2 As PropertyPathBuilder
            For Each item2 In Me.propertyPaths.Values
                item2.Indent = (MyBase.Indent + 4)
                sb.Append(item2.ToString)
                sb.AppendLine
            Next
            sb.Append(indent1)
            sb.AppendLine("End Class")
            Return sb.ToString
        End Function


        ' Fields
        Private propertyPaths As Dictionary(Of String, PropertyPathBuilder)
    End Class

    Private NotInheritable Class InterfaceBuilder
        Inherits ClassBuilder
        ' Methods
        Public Sub New(ByVal name As String, ByVal comments As List(Of String), ByVal indent As Integer)
            MyBase.New(name, comments, indent)
        End Sub

    End Class

    Public MustInherit Class MemberBuilder
        Inherits BuilderBase
        ' Methods
        Public Sub New(ByVal name As String, ByVal comments As List(Of String))
            MyBase.New(name, comments)
        End Sub

        Protected MustOverride Sub AppendObfuscatedPropertyContent(ByVal sb As StringBuilder)

        Public Overrides Function ToString() As String
            Dim indent As String = String.Empty.PadLeft(MyBase.Indent)
            Dim indent2 As String = String.Empty.PadLeft((MyBase.Indent + 4))
            Dim sb As New StringBuilder
            Dim line As String
            For Each line In MyBase.Comments
                sb.Append(indent)
                sb.AppendLine(line)
            Next
			sb.Append(indent)
			if supportObfuscation Then				
				sb.Append("Public Shared Readonly Property ")
				sb.Append(MyBase.Name)
				sb.AppendLine("() As String")
				sb.Append(indent2)
				sb.AppendLine("Get")
				Me.AppendObfuscatedPropertyContent(sb)
				sb.AppendLine
				sb.Append(indent2)
				sb.AppendLine("End Get")
				sb.Append(indent)
				sb.Append("End Property")				
			Else
				sb.AppendFormat("Public Const [{0}] As string = ""{1}""", Name, Name)
			End If
			sb.AppendLine
            Return sb.ToString
        End Function

    End Class

    Public NotInheritable Class MethodBuilder
        Inherits MemberBuilder
        ' Methods
        Public Sub New(ByVal codeMember As CodeFunction, ByVal comments As List(Of String))
            MyBase.New(codeMember.Name, comments)
            Me.codeMember = codeMember
        End Sub

        Protected Overrides Sub AppendObfuscatedPropertyContent(ByVal sb As StringBuilder)
            Dim indent3 As String = String.Empty.PadLeft((MyBase.Indent + 8))
            Dim indent4 As String = String.Empty.PadLeft((MyBase.Indent + 12))
            Dim indent5 As String = String.Empty.PadLeft((MyBase.Indent + &H10))
            If ((Me.codeMember.Access <> vsCMAccess.vsCMAccessPublic) AndAlso (Me.codeMember.Access <> vsCMAccess.vsCMAccessProject)) Then
                sb.Append(indent3)
                sb.AppendFormat("return ""{0}"";", MyBase.Name)
            Else
                Dim parameters As New List(Of String)(Me.codeMember.Parameters.Count)
                Dim parameter As CodeParameter
                For Each parameter In Me.codeMember.Parameters
                    parameters.Add(parameter.Type.AsFullName)
                Next
                Dim returnType As String = Me.codeMember.Type.AsFullName
                Dim isAction As Boolean = String.IsNullOrEmpty(returnType)
                Dim hasParameters As Boolean = (parameters.Count > 0)
                sb.Append(indent3)
                sb.Append("Return ")
                sb.Append("DanielVaughan.Metadata.ObfuscatedNameResolver.GetObfuscatedName(""")
                sb.Append(Me.codeMember.FullName)
                sb.AppendLine(""",")
                sb.Append(indent4)
                sb.AppendLine("x => {")
                sb.Append(indent5)
                If isAction Then
                    sb.Append("Expression<Action")
                Else
                    sb.Append("Expression<Func")
                End If
                If hasParameters Then
                    Dim parametersCount As Integer = parameters.Count
                    Dim i As Integer
                    For i = 0 To parametersCount - 1
                        sb.Append(parameters.Item(i))
                        If (i < (parametersCount - 1)) Then
                            sb.Append(","c)
                        End If
                    Next i
                    If Not ((parametersCount <= 0) OrElse String.IsNullOrEmpty(returnType)) Then
                        sb.Append(","c)
                    End If
                End If
                If Not isAction Then
                    sb.Append(returnType)
                    sb.Append(">"c)
                ElseIf hasParameters Then
                    sb.Append(">"c)
                End If
                Dim dictionaryKey As String = Me.codeMember.FullName.Substring(0, Me.codeMember.FullName.LastIndexOf("."c))
                If Me.codeMember.IsShared Then
                    sb.Append("> expression = () => ")
                    sb.Append(dictionaryKey)
                    sb.Append(".")
                Else
                    sb.Append("> expression = () => default(")
                    sb.Append(dictionaryKey)
                    sb.Append(").")
                End If
                sb.Append(MyBase.Name)
                If isAction Then
                    sb.Append("()")
                End If
                sb.AppendLine(";")
                sb.Append(indent5)
                sb.AppendLine("var body = (MethodCallExpression)expression.Body;")
                sb.Append(indent5)
                sb.AppendLine("return body.Method.Name;")
                sb.Append(indent4)
                sb.AppendLine("});")
            End If
        End Sub

        ' Fields
        Private codeMember As CodeFunction
    End Class

    Public NotInheritable Class PropertyBuilder
        Inherits MemberBuilder
        ' Methods
        Public Sub New(ByVal codeMember As CodeProperty, ByVal comments As List(Of String))
            MyBase.New(codeMember.Name, comments)
            Me.codeMember = codeMember
        End Sub

        Protected Overrides Sub AppendObfuscatedPropertyContent(ByVal sb As StringBuilder)
            Dim indent3 As String = String.Empty.PadLeft((3 * Indent))
            Dim indent4 As String = String.Empty.PadLeft((4 * Indent))
            Dim indent5 As String = String.Empty.PadLeft((5 * Indent))
           	sb.Append("Return """"")
			'System.Diagnostics.Debug.Assert(Not sb Is Nothing, "sb should not be null")
			'System.Diagnostics.Debug.Assert(Not Me.codeMember Is Nothing, "Me.codeMember should not be null")
			'System.Diagnostics.Debug.Assert(Not Me.codeMember.Getter Is Nothing, "Me.codeMember.Getter should not be null")
			'throw new Exception(Me.codeMember.Getter.Access & "")
'			If Me.codeMember.Getter.Access <> vsCMAccess.vsCMAccessPublic _
'				AndAlso Me.codeMember.Getter.Access <> vsCMAccess.vsCMAccessProject Then
                'sb.Append(indent3)
                'sb.AppendFormat("Return ""{0}"";", Name)
'            Else
'                Dim returnType As String = codeMember.Type.AsFullName
'                sb.Append(indent3)
'                sb.Append("Return ")
'                sb.Append("DanielVaughan.Metadata.ObfuscatedNameResolver.GetObfuscatedName(""")
'                sb.Append(Me.codeMember.FullName)
'                sb.AppendLine(""",")
'                sb.Append(indent4)
'                sb.AppendLine("x => {")
'                sb.Append(indent5)
'                sb.Append("Expression<Func<")
'                sb.Append(returnType)
'                Dim dictionaryKey As String = Me.codeMember.FullName.Substring(0, Me.codeMember.FullName.LastIndexOf("."c))
'                If IsStatic(Me.codeMember) Then
'                    sb.Append(">> expression = () => ")
'                    sb.Append(dictionaryKey)
'                    sb.Append(".")
'                Else
'                    sb.Append(">> expression = () => default(")
'                    sb.Append(dictionaryKey)
'                    sb.Append(").")
'                End If
'                sb.Append(MyBase.Name)
'                sb.AppendLine(";")
'                sb.Append(indent5)
'                sb.AppendLine("var body = (MemberExpression)expression.Body;")
'                sb.Append(indent5)
'                sb.AppendLine("return body.Member.Name;")
'                sb.Append(indent4)
'                sb.AppendLine("});")
'            End If
        End Sub

        ' Fields
        Private ReadOnly codeMember As CodeProperty
    End Class

    Public NotInheritable Class PropertyPathBuilder
        Inherits MemberBuilder
        ' Methods
        Public Sub New(ByVal codeMember As CodeProperty, ByVal comments As List(Of String))
            MyBase.New(codeMember.Name, comments)
            _codeMember = codeMember
        End Sub

        Protected Overrides Sub AppendObfuscatedPropertyContent(ByVal sb As StringBuilder)
            Dim indent3 As String = String.Empty.PadLeft((3 * Indent))
            Dim indent4 As String = String.Empty.PadLeft((4 * Indent))
            Dim indent5 As String = String.Empty.PadLeft((5 * Indent))

            If _codeMember.Access <> vsCMAccess.vsCMAccessPublic _ 
				AndAlso _codeMember.Access <> vsCMAccess.vsCMAccessProject Then
                sb.Append(indent3)
                sb.AppendFormat("Return new PropertyPath(""{0}"")", MyBase.Name)
            Else
               Dim returnType As String = _codeMember.Type.AsFullName
                sb.Append(indent3)
                sb.Append("Return new PropertyPath(")
                sb.Append("DanielVaughan.Metadata.ObfuscatedNameResolver.GetObfuscatedName(""")
                sb.Append(_codeMember.FullName)
                sb.AppendLine(""", Function (ByVal x As String)")

                sb.Append(indent4)
                'sb.Append("Dim expression As Expression(Of Func(Of ")
                'sb.Append(returnType)
                Dim dictionaryKey As String = _codeMember.FullName.Substring(0, _codeMember.FullName.LastIndexOf("."c))
                If IsStatic(_codeMember) Then
'					       Return New PropertyPath(ObfuscatedNameResolver.GetObfuscatedName("DanielVaughan.MetaGen.Demo.Folder1.Folder1StaticClass.StringStaticProperty", Function (ByVal x As String) 
'            Dim expression As Expression(Of Func(Of String)) = Expression.Lambda(Of Func(Of String))(Expression.Property(Nothing, DirectCast(GetMethod(Folder1StaticClass.get_StringStaticProperty), MethodInfo)), New ParameterExpression(0  - 1) {})
'            Dim body As MemberExpression = DirectCast(expression.Body, MemberExpression)
'            Return body.Member.Name
'        End Function), New Object(0  - 1) {})
					sb.AppendFormat("Dim expression As Expression(Of Func(Of {0})) = Expression.Lambda(Of Func(Of {1}))(Expression.Property(Nothing, DirectCast(GetMethod({2}), MethodInfo)), New ParameterExpression(0  - 1) {{}})", _
						returnType, returnType, dictionaryKey)
                Else
                    sb.AppendFormat("Dim expression As Expression(Of Func(Of {0})) = Expression.Lambda(Of Func(Of {1}))(Expression.Property(Expression.Constant(Nothing, GetType({2})), DirectCast(GetMethod({3}), MethodInfo)), New ParameterExpression(0  - 1) {{}})", _
						returnType, returnType, dictionaryKey, _codeMember.FullName)				
'					  Return New PropertyPath(ObfuscatedNameResolver.GetObfuscatedName("DanielVaughan.MetaGen.Demo.Folder1.Folder1Interface.Foo", Function (ByVal x As String) 
'        	Dim expression As Expression(Of Func(Of String)) = Expression.Lambda(Of Func(Of String))(Expression.Property(Expression.Constant(Nothing, GetType(Folder1Interface)), DirectCast(GetMethod(Folder1Interface.get_Foo), MethodInfo)), New ParameterExpression(0  - 1) {})
'        Dim body As MemberExpression = DirectCast(expression.Body, MemberExpression)
'        Return body.Member.Name
'    End Function), New Object(0  - 1) {})

                End If
                sb.AppendLine
				sb.Append(indent4)
				sb.AppendLine("Dim body As MemberExpression = DirectCast(expression.Body, MemberExpression)")
                sb.Append(indent4)
				sb.AppendLine("Return body.Member.Name")
                sb.Append(indent3)
				sb.AppendLine("End Function), New Object(0  - 1) {})")
                sb.Append(indent4)
            End If
        End Sub

        Public Overrides Function ToString() As String
            Dim indent As String = String.Empty.PadLeft(MyBase.Indent)
            Dim sb As New StringBuilder
            Dim line As String
            For Each line In MyBase.Comments
                sb.Append(indent)
                sb.AppendLine(line)
            Next
            Dim indent2 As String = String.Empty.PadLeft((MyBase.Indent + 4))
            Dim indent3 As String = String.Empty.PadLeft((MyBase.Indent + 8))
			
			sb.Append(indent)
			sb.Append("Public Readonly Shared Property ")
			sb.Append(Name)
			sb.AppendLine("Path() As PropertyPath")
			sb.Append(indent2)
			sb.AppendLine("Get")
			If Not supportObfuscation Then								
				sb.Append(indent3)
				sb.AppendFormat("Return new PropertyPath(""{0}"")", Name)				
			Else	
				Me.AppendObfuscatedPropertyContent(sb)
			End If
			sb.AppendLine()
			sb.Append(indent3)
			sb.AppendLine("End Get")
			sb.Append(indent2)
			sb.Append("End Property")
			sb.AppendLine
            Return sb.ToString
        End Function

        ' Fields
        Private _codeMember As CodeProperty
    End Class

   Public NotInheritable Class FieldBuilder
        Inherits MemberBuilder
        ' Methods
        Public Sub New(ByVal codeMember As CodeVariable, ByVal comments As List(Of String))
            MyBase.New(codeMember.Name, comments)
            Me.codeMember = codeMember
        End Sub

        Protected Overrides Sub AppendObfuscatedPropertyContent(ByVal sb As StringBuilder)
            Dim indent3 As String = String.Empty.PadLeft((MyBase.Indent + 8))
            If ((Me.codeMember.Access <> vsCMAccess.vsCMAccessPublic) AndAlso (Me.codeMember.Access <> vsCMAccess.vsCMAccessProject)) Then
                sb.Append(indent3)
                sb.AppendFormat("Return ""{0}""", MyBase.Name)
            Else
                Dim indent4 As String = String.Empty.PadLeft((MyBase.Indent + 12))
                Dim indent5 As String = String.Empty.PadLeft((MyBase.Indent + &H10))
                Dim returnType As String = Me.codeMember.Type.AsFullName
                sb.Append(indent3)
                sb.Append("Return ")
                sb.Append("DanielVaughan.Metadata.ObfuscatedNameResolver.GetObfuscatedName(""")
                sb.Append(Me.codeMember.FullName)
                sb.AppendLine(""",")
                sb.Append(indent4)
                sb.AppendLine("x => {")
                sb.Append(indent5)
                sb.Append("Expression<Func<")
                sb.Append(returnType)
                Dim dictionaryKey As String = Me.codeMember.FullName.Substring(0, Me.codeMember.FullName.LastIndexOf("."c))
                If Me.codeMember.IsShared Then
                    sb.Append(">> expression = () => ")
                    sb.Append(dictionaryKey)
                    sb.Append(".")
                Else
                    sb.Append(">> expression = () => default(")
                    sb.Append(dictionaryKey)
                    sb.Append(").")
                End If
                sb.Append(MyBase.Name)
                sb.AppendLine(";")
                sb.Append(indent5)
                sb.AppendLine("var body = (MemberExpression)expression.Body")
                sb.Append(indent5)
                sb.AppendLine("Return body.Member.Name")
                sb.Append(indent4)
                sb.AppendLine("});")
            End If
        End Sub

        ' Fields
        Private codeMember As CodeVariable
    End Class
#>

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, along with any associated source code and files, is licensed under The BSD License


Written By
Engineer
Switzerland Switzerland
Daniel is a former senior engineer in Technology and Research at the Office of the CTO at Microsoft, working on next generation systems.

Previously Daniel was a nine-time Microsoft MVP and co-founder of Outcoder, a Swiss software and consulting company.

Daniel is the author of Windows Phone 8 Unleashed and Windows Phone 7.5 Unleashed, both published by SAMS.

Daniel is the developer behind several acclaimed mobile apps including Surfy Browser for Android and Windows Phone. Daniel is the creator of a number of popular open-source projects, most notably Codon.

Would you like Daniel to bring value to your organisation? Please contact

Blog | Twitter


Xamarin Experts
Windows 10 Experts

Comments and Discussions