2021-02-18 08:14:27 +01:00
using System ;
2019-06-24 11:58:36 +02:00
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Text.RegularExpressions ;
2021-03-05 15:36:27 +01:00
using Umbraco.Cms.Core ;
2021-02-18 11:06:02 +01:00
using Umbraco.Cms.Core.Configuration.Models ;
2019-06-24 11:58:36 +02:00
2021-02-22 09:00:33 +01:00
namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building
2019-06-24 11:58:36 +02:00
{
/// <summary>
/// Implements a builder that works by writing text.
/// </summary>
2021-02-18 08:14:27 +01:00
public class TextBuilder : Builder
2019-06-24 11:58:36 +02:00
{
/// <summary>
/// Initializes a new instance of the <see cref="TextBuilder"/> class with a list of models to generate
/// and the result of code parsing.
/// </summary>
/// <param name="typeModels">The list of models to generate.</param>
2020-09-18 12:53:10 +02:00
public TextBuilder ( ModelsBuilderSettings config , IList < TypeModel > typeModels )
2019-10-28 18:02:52 +11:00
: base ( config , typeModels )
2019-06-24 11:58:36 +02:00
{ }
// internal for unit tests only
2020-07-13 21:52:11 +02:00
public TextBuilder ( )
2019-06-24 11:58:36 +02:00
{ }
/// <summary>
2019-10-28 18:02:52 +11:00
/// Outputs a generated model to a string builder.
/// </summary>
/// <param name="sb">The string builder.</param>
/// <param name="typeModel">The model to generate.</param>
2019-06-24 11:58:36 +02:00
public void Generate ( StringBuilder sb , TypeModel typeModel )
{
WriteHeader ( sb ) ;
foreach ( var t in TypesUsing )
sb . AppendFormat ( "using {0};\n" , t ) ;
sb . Append ( "\n" ) ;
sb . AppendFormat ( "namespace {0}\n" , GetModelsNamespace ( ) ) ;
sb . Append ( "{\n" ) ;
WriteContentType ( sb , typeModel ) ;
sb . Append ( "}\n" ) ;
}
/// <summary>
/// Outputs generated models to a string builder.
/// </summary>
/// <param name="sb">The string builder.</param>
/// <param name="typeModels">The models to generate.</param>
public void Generate ( StringBuilder sb , IEnumerable < TypeModel > typeModels )
{
WriteHeader ( sb ) ;
foreach ( var t in TypesUsing )
sb . AppendFormat ( "using {0};\n" , t ) ;
// assembly attributes marker
sb . Append ( "\n//ASSATTR\n" ) ;
sb . Append ( "\n" ) ;
sb . AppendFormat ( "namespace {0}\n" , GetModelsNamespace ( ) ) ;
sb . Append ( "{\n" ) ;
foreach ( var typeModel in typeModels )
{
WriteContentType ( sb , typeModel ) ;
sb . Append ( "\n" ) ;
}
sb . Append ( "}\n" ) ;
}
/// <summary>
/// Outputs an "auto-generated" header to a string builder.
/// </summary>
/// <param name="sb">The string builder.</param>
public static void WriteHeader ( StringBuilder sb )
{
TextHeaderWriter . WriteHeader ( sb ) ;
}
// writes an attribute that identifies code generated by a tool
// (helps reduce warnings, tools such as FxCop use it)
// see https://github.com/zpqrtbnk/Zbu.ModelsBuilder/issues/107
// see https://docs.microsoft.com/en-us/dotnet/api/system.codedom.compiler.generatedcodeattribute
// see https://blogs.msdn.microsoft.com/codeanalysis/2007/04/27/correct-usage-of-the-compilergeneratedattribute-and-the-generatedcodeattribute/
//
// note that the blog post above clearly states that "Nor should it be applied at the type level if the type being generated is a partial class."
// and since our models are partial classes, we have to apply the attribute against the individual members, not the class itself.
//
private static void WriteGeneratedCodeAttribute ( StringBuilder sb , string tabs )
{
2020-03-07 21:33:35 +00:00
sb . AppendFormat ( "{0}[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Umbraco.ModelsBuilder.Embedded\", \"{1}\")]\n" , tabs , ApiVersion . Current . Version ) ;
2019-06-24 11:58:36 +02:00
}
2021-07-13 01:26:03 +02:00
// writes an attribute that specifies that an output may be null.
// (useful for consuming projects with nullable reference types enabled)
private static void WriteMaybeNullAttribute ( StringBuilder sb , string tabs , bool isReturn = false )
{
sb . AppendFormat ( "{0}[{1}global::System.Diagnostics.CodeAnalysis.MaybeNull]\n" , tabs , isReturn ? "return: " : "" ) ;
}
2019-06-24 11:58:36 +02:00
private void WriteContentType ( StringBuilder sb , TypeModel type )
{
string sep ;
if ( type . IsMixin )
{
// write the interface declaration
sb . AppendFormat ( "\t// Mixin Content Type with alias \"{0}\"\n" , type . Alias ) ;
if ( ! string . IsNullOrWhiteSpace ( type . Name ) )
sb . AppendFormat ( "\t/// <summary>{0}</summary>\n" , XmlCommentString ( type . Name ) ) ;
sb . AppendFormat ( "\tpublic partial interface I{0}" , type . ClrName ) ;
var implements = type . BaseType = = null
? ( type . HasBase ? null : ( type . IsElement ? "PublishedElement" : "PublishedContent" ) )
: type . BaseType . ClrName ;
if ( implements ! = null )
sb . AppendFormat ( " : I{0}" , implements ) ;
// write the mixins
sep = implements = = null ? ":" : "," ;
foreach ( var mixinType in type . DeclaringInterfaces . OrderBy ( x = > x . ClrName ) )
{
sb . AppendFormat ( "{0} I{1}" , sep , mixinType . ClrName ) ;
sep = "," ;
}
sb . Append ( "\n\t{\n" ) ;
// write the properties - only the local (non-ignored) ones, we're an interface
var more = false ;
foreach ( var prop in type . Properties . OrderBy ( x = > x . ClrName ) )
{
if ( more ) sb . Append ( "\n" ) ;
more = true ;
WriteInterfaceProperty ( sb , prop ) ;
}
sb . Append ( "\t}\n\n" ) ;
}
// write the class declaration
if ( ! string . IsNullOrWhiteSpace ( type . Name ) )
sb . AppendFormat ( "\t/// <summary>{0}</summary>\n" , XmlCommentString ( type . Name ) ) ;
// cannot do it now. see note in ImplementContentTypeAttribute
//if (!type.HasImplement)
// sb.AppendFormat("\t[ImplementContentType(\"{0}\")]\n", type.Alias);
sb . AppendFormat ( "\t[PublishedModel(\"{0}\")]\n" , type . Alias ) ;
sb . AppendFormat ( "\tpublic partial class {0}" , type . ClrName ) ;
var inherits = type . HasBase
? null // has its own base already
: ( type . BaseType = = null
? GetModelsBaseClassName ( type )
: type . BaseType . ClrName ) ;
if ( inherits ! = null )
sb . AppendFormat ( " : {0}" , inherits ) ;
sep = inherits = = null ? ":" : "," ;
if ( type . IsMixin )
{
// if it's a mixin it implements its own interface
sb . AppendFormat ( "{0} I{1}" , sep , type . ClrName ) ;
}
else
{
// write the mixins, if any, as interfaces
// only if not a mixin because otherwise the interface already has them already
foreach ( var mixinType in type . DeclaringInterfaces . OrderBy ( x = > x . ClrName ) )
{
sb . AppendFormat ( "{0} I{1}" , sep , mixinType . ClrName ) ;
sep = "," ;
}
}
// begin class body
sb . Append ( "\n\t{\n" ) ;
// write the constants & static methods
// as 'new' since parent has its own - or maybe not - disable warning
sb . Append ( "\t\t// helpers\n" ) ;
sb . Append ( "#pragma warning disable 0109 // new is redundant\n" ) ;
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
sb . AppendFormat ( "\t\tpublic new const string ModelTypeAlias = \"{0}\";\n" ,
type . Alias ) ;
var itemType = type . IsElement ? TypeModel . ItemTypes . Content : type . ItemType ; // fixme
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
sb . AppendFormat ( "\t\tpublic new const PublishedItemType ModelItemType = PublishedItemType.{0};\n" ,
itemType ) ;
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
2021-07-13 01:26:03 +02:00
WriteMaybeNullAttribute ( sb , "\t\t" , true ) ;
2020-08-03 14:16:58 +02:00
sb . Append ( "\t\tpublic new static IPublishedContentType GetModelContentType(IPublishedSnapshotAccessor publishedSnapshotAccessor)\n" ) ;
sb . Append ( "\t\t\t=> PublishedModelUtility.GetModelContentType(publishedSnapshotAccessor, ModelItemType, ModelTypeAlias);\n" ) ;
2019-06-24 11:58:36 +02:00
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
2021-07-13 01:26:03 +02:00
WriteMaybeNullAttribute ( sb , "\t\t" , true ) ;
2020-08-03 14:16:58 +02:00
sb . AppendFormat ( "\t\tpublic static IPublishedPropertyType GetModelPropertyType<TValue>(IPublishedSnapshotAccessor publishedSnapshotAccessor, Expression<Func<{0}, TValue>> selector)\n" ,
2019-06-24 11:58:36 +02:00
type . ClrName ) ;
2020-08-03 14:16:58 +02:00
sb . Append ( "\t\t\t=> PublishedModelUtility.GetModelPropertyType(GetModelContentType(publishedSnapshotAccessor), selector);\n" ) ;
2019-06-24 11:58:36 +02:00
sb . Append ( "#pragma warning restore 0109\n\n" ) ;
2020-09-08 15:17:57 +02:00
sb . Append ( "\t\tprivate IPublishedValueFallback _publishedValueFallback;" ) ;
2019-06-24 11:58:36 +02:00
// write the ctor
2021-05-06 10:58:45 +02:00
sb . AppendFormat ( "\n\n\t\t// ctor\n\t\tpublic {0}(IPublished{1} content, IPublishedValueFallback publishedValueFallback)\n\t\t\t: base(content, publishedValueFallback)\n\t\t{{\n\t\t\t_publishedValueFallback = publishedValueFallback;\n\t\t}}\n\n" ,
2019-06-24 11:58:36 +02:00
type . ClrName , type . IsElement ? "Element" : "Content" ) ;
// write the properties
sb . Append ( "\t\t// properties\n" ) ;
WriteContentTypeProperties ( sb , type ) ;
// close the class declaration
sb . Append ( "\t}\n" ) ;
}
private void WriteContentTypeProperties ( StringBuilder sb , TypeModel type )
{
var staticMixinGetters = true ;
// write the properties
foreach ( var prop in type . Properties . OrderBy ( x = > x . ClrName ) )
WriteProperty ( sb , type , prop , staticMixinGetters & & type . IsMixin ? type . ClrName : null ) ;
// no need to write the parent properties since we inherit from the parent
// and the parent defines its own properties. need to write the mixins properties
// since the mixins are only interfaces and we have to provide an implementation.
// write the mixins properties
foreach ( var mixinType in type . ImplementingInterfaces . OrderBy ( x = > x . ClrName ) )
foreach ( var prop in mixinType . Properties . OrderBy ( x = > x . ClrName ) )
if ( staticMixinGetters )
WriteMixinProperty ( sb , prop , mixinType . ClrName ) ;
else
WriteProperty ( sb , mixinType , prop ) ;
}
private void WriteMixinProperty ( StringBuilder sb , PropertyModel property , string mixinClrName )
{
sb . Append ( "\n" ) ;
// Adds xml summary to each property containing
// property name and property description
if ( ! string . IsNullOrWhiteSpace ( property . Name ) | | ! string . IsNullOrWhiteSpace ( property . Description ) )
{
sb . Append ( "\t\t///<summary>\n" ) ;
if ( ! string . IsNullOrWhiteSpace ( property . Description ) )
sb . AppendFormat ( "\t\t/// {0}: {1}\n" , XmlCommentString ( property . Name ) , XmlCommentString ( property . Description ) ) ;
else
sb . AppendFormat ( "\t\t/// {0}\n" , XmlCommentString ( property . Name ) ) ;
sb . Append ( "\t\t///</summary>\n" ) ;
}
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
sb . AppendFormat ( "\t\t[ImplementPropertyType(\"{0}\")]\n" , property . Alias ) ;
2021-02-21 16:19:48 +00:00
sb . Append ( "\t\tpublic virtual " ) ;
2019-06-24 11:58:36 +02:00
WriteClrType ( sb , property . ClrTypeName ) ;
sb . AppendFormat ( " {0} => " ,
property . ClrName ) ;
WriteNonGenericClrType ( sb , GetModelsNamespace ( ) + "." + mixinClrName ) ;
2021-01-29 10:30:28 +01:00
sb . AppendFormat ( ".{0}(this, _publishedValueFallback);\n" ,
2019-06-24 11:58:36 +02:00
MixinStaticGetterName ( property . ClrName ) ) ;
}
private static string MixinStaticGetterName ( string clrName )
{
return string . Format ( "Get{0}" , clrName ) ;
}
private void WriteProperty ( StringBuilder sb , TypeModel type , PropertyModel property , string mixinClrName = null )
{
var mixinStatic = mixinClrName ! = null ;
sb . Append ( "\n" ) ;
if ( property . Errors ! = null )
{
sb . Append ( "\t\t/*\n" ) ;
sb . Append ( "\t\t * THIS PROPERTY CANNOT BE IMPLEMENTED, BECAUSE:\n" ) ;
sb . Append ( "\t\t *\n" ) ;
var first = true ;
foreach ( var error in property . Errors )
{
if ( first ) first = false ;
else sb . Append ( "\t\t *\n" ) ;
foreach ( var s in SplitError ( error ) )
{
sb . Append ( "\t\t * " ) ;
sb . Append ( s ) ;
sb . Append ( "\n" ) ;
}
}
sb . Append ( "\t\t *\n" ) ;
sb . Append ( "\n" ) ;
}
// Adds xml summary to each property containing
// property name and property description
if ( ! string . IsNullOrWhiteSpace ( property . Name ) | | ! string . IsNullOrWhiteSpace ( property . Description ) )
{
sb . Append ( "\t\t///<summary>\n" ) ;
if ( ! string . IsNullOrWhiteSpace ( property . Description ) )
sb . AppendFormat ( "\t\t/// {0}: {1}\n" , XmlCommentString ( property . Name ) , XmlCommentString ( property . Description ) ) ;
else
sb . AppendFormat ( "\t\t/// {0}\n" , XmlCommentString ( property . Name ) ) ;
sb . Append ( "\t\t///</summary>\n" ) ;
}
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
2021-07-13 01:26:03 +02:00
if ( ! property . ModelClrType . IsValueType )
WriteMaybeNullAttribute ( sb , "\t\t" ) ;
2019-06-24 11:58:36 +02:00
sb . AppendFormat ( "\t\t[ImplementPropertyType(\"{0}\")]\n" , property . Alias ) ;
if ( mixinStatic )
{
2021-02-21 16:19:48 +00:00
sb . Append ( "\t\tpublic virtual " ) ;
2019-06-24 11:58:36 +02:00
WriteClrType ( sb , property . ClrTypeName ) ;
2021-01-29 10:30:28 +01:00
sb . AppendFormat ( " {0} => {1}(this, _publishedValueFallback);\n" ,
2019-06-24 11:58:36 +02:00
property . ClrName , MixinStaticGetterName ( property . ClrName ) ) ;
}
else
{
2021-02-21 16:19:48 +00:00
sb . Append ( "\t\tpublic virtual " ) ;
2019-06-24 11:58:36 +02:00
WriteClrType ( sb , property . ClrTypeName ) ;
sb . AppendFormat ( " {0} => this.Value" ,
property . ClrName ) ;
if ( property . ModelClrType ! = typeof ( object ) )
{
sb . Append ( "<" ) ;
WriteClrType ( sb , property . ClrTypeName ) ;
sb . Append ( ">" ) ;
}
2020-09-02 14:44:01 +02:00
sb . AppendFormat ( "(_publishedValueFallback, \"{0}\");\n" ,
2019-06-24 11:58:36 +02:00
property . Alias ) ;
}
if ( property . Errors ! = null )
{
sb . Append ( "\n" ) ;
sb . Append ( "\t\t *\n" ) ;
sb . Append ( "\t\t */\n" ) ;
}
if ( ! mixinStatic ) return ;
var mixinStaticGetterName = MixinStaticGetterName ( property . ClrName ) ;
2019-10-28 18:02:52 +11:00
//if (type.StaticMixinMethods.Contains(mixinStaticGetterName)) return;
2019-06-24 11:58:36 +02:00
sb . Append ( "\n" ) ;
if ( ! string . IsNullOrWhiteSpace ( property . Name ) )
sb . AppendFormat ( "\t\t/// <summary>Static getter for {0}</summary>\n" , XmlCommentString ( property . Name ) ) ;
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
2021-07-13 01:26:03 +02:00
if ( ! property . ModelClrType . IsValueType )
WriteMaybeNullAttribute ( sb , "\t\t" , true ) ;
2019-06-24 11:58:36 +02:00
sb . Append ( "\t\tpublic static " ) ;
WriteClrType ( sb , property . ClrTypeName ) ;
2021-01-29 10:30:28 +01:00
sb . AppendFormat ( " {0}(I{1} that, IPublishedValueFallback publishedValueFallback) => that.Value" ,
2019-06-24 11:58:36 +02:00
mixinStaticGetterName , mixinClrName ) ;
if ( property . ModelClrType ! = typeof ( object ) )
{
sb . Append ( "<" ) ;
WriteClrType ( sb , property . ClrTypeName ) ;
sb . Append ( ">" ) ;
}
2021-01-29 10:30:28 +01:00
sb . AppendFormat ( "(publishedValueFallback, \"{0}\");\n" ,
2019-06-24 11:58:36 +02:00
property . Alias ) ;
}
private static IEnumerable < string > SplitError ( string error )
{
var p = 0 ;
while ( p < error . Length )
{
var n = p + 50 ;
while ( n < error . Length & & error [ n ] ! = ' ' ) n + + ;
if ( n > = error . Length ) break ;
yield return error . Substring ( p , n - p ) ;
p = n + 1 ;
}
if ( p < error . Length )
yield return error . Substring ( p ) ;
}
private void WriteInterfaceProperty ( StringBuilder sb , PropertyModel property )
{
if ( property . Errors ! = null )
{
sb . Append ( "\t\t/*\n" ) ;
sb . Append ( "\t\t * THIS PROPERTY CANNOT BE IMPLEMENTED, BECAUSE:\n" ) ;
sb . Append ( "\t\t *\n" ) ;
var first = true ;
foreach ( var error in property . Errors )
{
if ( first ) first = false ;
else sb . Append ( "\t\t *\n" ) ;
foreach ( var s in SplitError ( error ) )
{
sb . Append ( "\t\t * " ) ;
sb . Append ( s ) ;
sb . Append ( "\n" ) ;
}
}
sb . Append ( "\t\t *\n" ) ;
sb . Append ( "\n" ) ;
}
if ( ! string . IsNullOrWhiteSpace ( property . Name ) )
sb . AppendFormat ( "\t\t/// <summary>{0}</summary>\n" , XmlCommentString ( property . Name ) ) ;
WriteGeneratedCodeAttribute ( sb , "\t\t" ) ;
2021-07-13 01:26:03 +02:00
if ( ! property . ModelClrType . IsValueType )
WriteMaybeNullAttribute ( sb , "\t\t" ) ;
2019-06-24 11:58:36 +02:00
sb . Append ( "\t\t" ) ;
WriteClrType ( sb , property . ClrTypeName ) ;
sb . AppendFormat ( " {0} {{ get; }}\n" ,
property . ClrName ) ;
if ( property . Errors ! = null )
{
sb . Append ( "\n" ) ;
sb . Append ( "\t\t *\n" ) ;
sb . Append ( "\t\t */\n" ) ;
}
}
// internal for unit tests
2020-07-13 21:52:11 +02:00
public void WriteClrType ( StringBuilder sb , Type type )
2019-06-24 11:58:36 +02:00
{
var s = type . ToString ( ) ;
if ( type . IsGenericType )
{
var p = s . IndexOf ( '`' ) ;
WriteNonGenericClrType ( sb , s . Substring ( 0 , p ) ) ;
sb . Append ( "<" ) ;
var args = type . GetGenericArguments ( ) ;
for ( var i = 0 ; i < args . Length ; i + + )
{
if ( i > 0 ) sb . Append ( ", " ) ;
WriteClrType ( sb , args [ i ] ) ;
}
sb . Append ( ">" ) ;
}
else
{
WriteNonGenericClrType ( sb , s ) ;
}
}
internal void WriteClrType ( StringBuilder sb , string type )
{
var p = type . IndexOf ( '<' ) ;
if ( type . Contains ( '<' ) )
{
WriteNonGenericClrType ( sb , type . Substring ( 0 , p ) ) ;
sb . Append ( "<" ) ;
2021-03-05 15:36:27 +01:00
var args = type . Substring ( p + 1 ) . TrimEnd ( Constants . CharArrays . GreaterThan ) . Split ( Constants . CharArrays . Comma ) ; // fixme will NOT work with nested generic types
2019-06-24 11:58:36 +02:00
for ( var i = 0 ; i < args . Length ; i + + )
{
if ( i > 0 ) sb . Append ( ", " ) ;
WriteClrType ( sb , args [ i ] ) ;
}
sb . Append ( ">" ) ;
}
else
{
WriteNonGenericClrType ( sb , type ) ;
}
}
private void WriteNonGenericClrType ( StringBuilder sb , string s )
{
// map model types
s = Regex . Replace ( s , @"\{(.*)\}\[\*\]" , m = > ModelsMap [ m . Groups [ 1 ] . Value + "[]" ] ) ;
// takes care eg of "System.Int32" vs. "int"
if ( TypesMap . TryGetValue ( s , out string typeName ) )
{
sb . Append ( typeName ) ;
return ;
}
// if full type name matches a using clause, strip
// so if we want Umbraco.Core.Models.IPublishedContent
// and using Umbraco.Core.Models, then we just need IPublishedContent
typeName = s ;
string typeUsing = null ;
var p = typeName . LastIndexOf ( '.' ) ;
if ( p > 0 )
{
var x = typeName . Substring ( 0 , p ) ;
if ( Using . Contains ( x ) )
{
typeName = typeName . Substring ( p + 1 ) ;
typeUsing = x ;
}
else if ( x = = ModelsNamespace ) // that one is used by default
{
typeName = typeName . Substring ( p + 1 ) ;
typeUsing = ModelsNamespace ;
}
}
// nested types *after* using
typeName = typeName . Replace ( "+" , "." ) ;
// symbol to test is the first part of the name
// so if type name is Foo.Bar.Nil we want to ensure that Foo is not ambiguous
p = typeName . IndexOf ( '.' ) ;
var symbol = p > 0 ? typeName . Substring ( 0 , p ) : typeName ;
// what we should find - WITHOUT any generic <T> thing - just the type
// no 'using' = the exact symbol
// a 'using' = using.symbol
var match = typeUsing = = null ? symbol : ( typeUsing + "." + symbol ) ;
// if not ambiguous, be happy
if ( ! IsAmbiguousSymbol ( symbol , match ) )
{
sb . Append ( typeName ) ;
return ;
}
// symbol is ambiguous
// if no 'using', must prepend global::
if ( typeUsing = = null )
{
sb . Append ( "global::" ) ;
sb . Append ( s . Replace ( "+" , "." ) ) ;
return ;
}
// could fullname be non-ambiguous?
// note: all-or-nothing, not trying to segment the using clause
typeName = s . Replace ( "+" , "." ) ;
p = typeName . IndexOf ( '.' ) ;
symbol = typeName . Substring ( 0 , p ) ;
match = symbol ;
// still ambiguous, must prepend global::
if ( IsAmbiguousSymbol ( symbol , match ) )
sb . Append ( "global::" ) ;
sb . Append ( typeName ) ;
}
private static string XmlCommentString ( string s )
{
return s . Replace ( '<' , '{' ) . Replace ( '>' , '}' ) . Replace ( '\r' , ' ' ) . Replace ( '\n' , ' ' ) ;
}
private static readonly IDictionary < string , string > TypesMap = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase )
{
{ "System.Int16" , "short" } ,
{ "System.Int32" , "int" } ,
{ "System.Int64" , "long" } ,
{ "System.String" , "string" } ,
{ "System.Object" , "object" } ,
{ "System.Boolean" , "bool" } ,
{ "System.Void" , "void" } ,
{ "System.Char" , "char" } ,
{ "System.Byte" , "byte" } ,
{ "System.UInt16" , "ushort" } ,
{ "System.UInt32" , "uint" } ,
{ "System.UInt64" , "ulong" } ,
{ "System.SByte" , "sbyte" } ,
{ "System.Single" , "float" } ,
{ "System.Double" , "double" } ,
{ "System.Decimal" , "decimal" }
} ;
}
}