Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion src/ExpressionTranslator/ExpressionTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Xml.Linq;

namespace ExpressionDebugger
{
Expand Down Expand Up @@ -1268,6 +1269,88 @@ public Expression VisitLambda(LambdaExpression node, LambdaType type, string? me
}
}

public Expression VisitLambdaForGenerateMappers(LambdaExpression node, LambdaType type, Type InterfaceType, string? methodName = null,
bool isInternal = false)
{
VisitLambda(node, type, methodName, isInternal);

if (!isInternal)
isInternal = node.ReturnType.GetTypeInfo().IsNotPublic ||
node.Parameters.Any(it => it.Type.GetTypeInfo().IsNotPublic);

if(!isInternal)
return node; // skip create interface implimentation if public only

if (type == LambdaType.PrivateLambda || type == LambdaType.PublicLambda)
{
_inlineCount++;
if (type == LambdaType.PublicLambda)
{
var name = methodName != null ? $"{InterfaceType.FullName}.{methodName}" : "Main";
WriteLine();
var funcType = MakeDelegateType(node.ReturnType, node.Parameters.Select(it => it.Type).ToArray());
var exprType = typeof(Expression<>).MakeGenericType(funcType);
Write(Translate(exprType), " ", name, " => ");
}

IList<ParameterExpression> args;
if (node.Parameters.Count == 1)
{
args = new List<ParameterExpression>();
var arg = VisitParameter(node.Parameters[0]);
args.Add((ParameterExpression)arg);
}
else
{
args = VisitArguments("(", node.Parameters.ToList(), p => (ParameterExpression)VisitParameter(p),
")");
}

Write(" => ");
var body = VisitGroup(node.Body, ExpressionType.Quote);
if (type == LambdaType.PublicLambda)
Write(";");
_inlineCount--;
return Expression.Lambda(body, node.Name, node.TailCall, args);
}
else
{
var name = methodName != null ? $"{InterfaceType.FullName}.{methodName}" : "Main";
if (type == LambdaType.PublicMethod || type == LambdaType.ExtensionMethod)
{
if (!isInternal)
isInternal = node.ReturnType.GetTypeInfo().IsNotPublic ||
node.Parameters.Any(it => it.Type.GetTypeInfo().IsNotPublic);
WriteLine();
Methods[name] = node.Type;
}
else
{
name = GetName(node, name);
WriteModifierNextLine("private");
}

Write(Translate(node.ReturnType), " ", name);
var open = "(";
if (type == LambdaType.ExtensionMethod)
{
if (Definitions?.IsStatic != true)
throw new InvalidOperationException("Extension method requires static class");
if (node.Parameters.Count == 0)
throw new InvalidOperationException("Extension method requires at least 1 parameter");
open = "(this ";
}

var args = VisitArguments(open, node.Parameters, VisitParameterDeclaration, ")");
Indent();
var body = VisitBody(node.Body, true);

Outdent();

return Expression.Lambda(body, name, node.TailCall, args);
}
}

private HashSet<LambdaExpression>? _visitedLambda;
private int _writerLevel;

Expand Down Expand Up @@ -1865,9 +1948,16 @@ public override string ToString()
WriteNextLine("using ", ns, ";");
}

WriteLine();
}

foreach (var ns in Definitions.GeneratedAttributes.Select(x => x.NameSpace).Distinct())
{
WriteNextLine("using ", ns, ";");
}

if(_usings != null || Definitions.GeneratedAttributes.Count != 0)
WriteLine();

// NOTE: type alias cannot solve all name conflicted case, user should use PrintFullTypeName
// keep logic here for compatibility
if (_typeNames != null)
Expand All @@ -1891,6 +1981,11 @@ public override string ToString()
Indent();
}

foreach (var gAttr in Definitions.GeneratedAttributes)
{
WriteNextLine(gAttr.Implimentation);
}

var isInternal = Definitions.IsInternal;
if (!isInternal)
isInternal = Definitions.Implements?.Any(it =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace ExpressionDebugger.Helpers.GeneratedAttributes
{
public interface IGeneratedAttribute
{
public string NameSpace { get;}
public string Declaration { get;}
public string Implimentation { get; }
public string FileName { get;}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Text;

namespace ExpressionDebugger.Helpers.GeneratedAttributes
{
public class MapsterToolGeneratedMapperAttribute : GeneratedBase, IGeneratedAttribute
{
private readonly StringBuilder _Declaration;
private readonly string _NameSpace;

public string NameSpace => _NameSpace;

public string Declaration => _Declaration.ToString();

public string Implimentation => "[MapsterToolGeneratedMapper]";

public string FileName => "MapsterToolGeneratedMapperAttribute";

public MapsterToolGeneratedMapperAttribute(string extendedNameSpace)
{
if (String.IsNullOrEmpty(extendedNameSpace))
throw new ArgumentNullException("Extended namespace not specified or is null/empty string");

_NameSpace = $"Mapster.Generated.Attributes.{extendedNameSpace}";

_Declaration = new StringBuilder();

_Declaration.Append("using System;\r\n\r\n");
_Declaration.Append($"namespace {NameSpace}");
_Declaration.Append("\r\n{\r\n public sealed class MapsterToolGeneratedMapperAttribute : Attribute\r\n {\r\n\r\n }\r\n} ");
}

}
}
18 changes: 18 additions & 0 deletions src/ExpressionTranslator/Helpers/GeneratedBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace ExpressionDebugger.Helpers
{
public abstract class GeneratedBase
{
public override bool Equals(object obj)
{
if(obj is null)
return base.Equals(obj);
else
return this.GetType() == obj.GetType();
}

public override int GetHashCode()
{
return this.GetType().GetHashCode();
}
}
}
33 changes: 33 additions & 0 deletions src/ExpressionTranslator/Helpers/MemberInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Reflection;

namespace ExpressionDebugger.Helpers
{
public static class MemberInfoExtensions
{
public static bool IsPublicOrInternal(this MethodInfo method)
{
if (method == null) throw new ArgumentNullException(nameof(method));

return !method.IsPrivate
&& !method.IsFamily
&& !method.IsFamilyOrAssembly
&& !method.IsFamilyAndAssembly
&& (method.IsPublic || true);
}



public static bool IsGetterPublicOrInternal(this PropertyInfo property)
{
if (property == null) throw new ArgumentNullException(nameof(property));

MethodInfo? getMethod = property.GetMethod;

if (getMethod == null) return false;

return getMethod.IsPublicOrInternal();
}
}

}
56 changes: 56 additions & 0 deletions src/ExpressionTranslator/Helpers/RandomNamespaceGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Text;

namespace ExpressionDebugger.Helpers
{
public static class RandomNamespaceGenerator
{
private static readonly Random _random = new Random();
private const string Consonants = "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ";
private const string Vowels = "aeiouAEIOU";
private const string Digits = "0123456789";

public static string Generate(int minParts = 2, int maxParts = 4)
{
if (minParts < 1) minParts = 1;
if (maxParts < minParts) maxParts = minParts;

int partsCount = _random.Next(minParts, maxParts + 1);
var sb = new StringBuilder();

for (int i = 0; i < partsCount; i++)
{
if (i > 0) sb.Append('.');
sb.Append(GeneratePart());
}

return sb.ToString();
}

private static string GeneratePart(int minLength = 2, int maxLength = 10)
{
if (minLength < 1) minLength = 1;
if (maxLength < minLength) maxLength = minLength;

int length = _random.Next(minLength, maxLength + 1);
var sb = new StringBuilder(length);

sb.Append(Consonants[_random.Next(Consonants.Length)]);

for (int i = 1; i < length; i++)
{
string pool = (i % 2 == 0) ? Vowels : Consonants;

if (_random.NextDouble() < 0.1)
{
pool = Digits;
}

sb.Append(pool[_random.Next(pool.Length)]);
}

return sb.ToString();
}
}
}

4 changes: 3 additions & 1 deletion src/ExpressionTranslator/TypeDefinitions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using ExpressionDebugger.Helpers.GeneratedAttributes;
using System;
using System.Collections.Generic;

namespace ExpressionDebugger
Expand All @@ -12,6 +13,7 @@ public class TypeDefinitions
public IEnumerable<Type>? Implements { get; set; }
public bool PrintFullTypeName { get; set; }
public bool IsRecordType { get; set; }
public HashSet<IGeneratedAttribute> GeneratedAttributes { get; set; } = new HashSet<IGeneratedAttribute>();

/// <summary>
/// Set to 2 to mark all properties as nullable
Expand Down
3 changes: 3 additions & 0 deletions src/Mapster.Tool/MapperOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public class MapperOptions
[Option('N', "nullableDirective", Required = false, HelpText = "Set true to add \"#nullable enable\" to the top of generated mapper files")]
public bool GenerateNullableDirective { get; set; }

[Option('H', "extNamespace", Required = false, HelpText = "Specify namespace to activate and generate additional features")]
public string? CreateHelpers { get; set; }

[Usage(ApplicationAlias = "dotnet mapster mapper")]
public static IEnumerable<Example> Examples =>
new List<Example>
Expand Down
Loading
Loading