using System.Collections.Generic;
using System.Linq;
using com.github.javaparser.ast.expr;
using JavaToCSharp.Expressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using ast = com.github.javaparser.ast;
namespace JavaToCSharp;
public static class TypeHelper
{
private static readonly Dictionary _typeNameConversions = new()
{
// Simple types
["boolean"] = "bool",
["Boolean"] = "bool",
["ICloseable"] = "IDisposable",
["Integer"] = "int",
["Long"] = "long",
["Float"] = "float",
["String"] = "string",
["Object"] = "object",
["AutoCloseable"] = "IDisposable",
// Generic types
["ArrayList"] = "List",
["List"] = "IList",
["Map"] = "Dictionary",
["Set"] = "HashSet",
["Iterator"] = "IEnumerator",
// Exceptions
["AlreadyClosedException"] = "ObjectDisposedException",
["Error"] = "Exception",
["IllegalArgumentException"] = "ArgumentException",
["IllegalStateException"] = "InvalidOperationException",
["UnsupportedOperationException"] = "NotSupportedException",
["RuntimeException"] = "Exception",
["AccessDeniedException"] = "UnauthorizedAccessException",
["AssertionError"] = "InvalidOperationException",
["NullPointerException"] = "NullReferenceException",
["UncheckedIOException"] = "IOException",
["EOFException"] = "EndOfStreamException",
["NoSuchFileException"] = "FileNotFoundException",
};
public static void AddOrUpdateTypeNameConversions(string key, string value)
{
_typeNameConversions[key] = value;
}
public static string ConvertTypeOf(ast.nodeTypes.NodeWithType typedNode)
{
return ConvertType(typedNode.getType().toString());
}
public static string ConvertType(ast.type.Type type)
{
return ConvertType(type.toString());
}
public static string ConvertType(string typeName)
{
return TypeNameParser.ParseTypeName(typeName, s =>
{
if (_typeNameConversions.TryGetValue(s, out string? converted))
{
return converted;
}
return s;
});
}
public static string Capitalize(string name)
{
var parts = name.Split('.');
var joined = System.String.Join(".", parts.Select(i =>
{
if (i.Length == 1)
return i.ToUpper();
else
return i[0].ToString().ToUpper() + i[1..];
}));
return joined;
}
public static string EscapeIdentifier(string name)
{
// @ (C# Reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim
return name switch {
"string" or "ref" or "object" or "int" or "short" or "float" or "long" or "double" or "decimal" or "in" or
"out" or "byte" or "class" or "delegate" or "params" or "is" or "as" or "base" or "namespace" or "event" or
"lock" or "operator" or "override" => "@" + name,
_ => name,
};
}
public static string ReplaceCommonMethodNames(string name)
{
return name.ToLower() switch {
"hashcode" => "GetHashCode",
"getclass" => "GetType",
"close" => "Dispose",
_ => name,
};
}
public static TypeSyntax GetSyntaxFromType(ast.type.ClassOrInterfaceType type)
{
string typeName = ConvertType(type.getNameAsString());
var typeArgs = type.getTypeArguments().FromOptional()?.ToList();
TypeSyntax typeSyntax;
if (typeArgs is { Count: > 0 })
{
typeSyntax = SyntaxFactory.GenericName(typeName)
.AddTypeArgumentListArguments(typeArgs.Select(t => SyntaxFactory.ParseTypeName(ConvertType(t))).ToArray());
}
else
{
typeSyntax = SyntaxFactory.ParseTypeName(typeName);
}
return typeSyntax;
}
public static ArgumentListSyntax GetSyntaxFromArguments(ConversionContext context, java.util.List args)
{
return SyntaxFactory.ArgumentList(GetSeparatedListFromArguments(context, args));
}
private static SeparatedSyntaxList GetSeparatedListFromArguments(ConversionContext context, java.util.List args)
{
return GetSeparatedListFromArguments(context, args.OfType());
}
private static SeparatedSyntaxList GetSeparatedListFromArguments(ConversionContext context, IEnumerable args)
{
var argSyntaxes = new List();
foreach (var arg in args)
{
var argSyntax = ExpressionVisitor.VisitExpression(context, arg);
if (argSyntax is null)
{
continue;
}
argSyntaxes.Add(SyntaxFactory.Argument(argSyntax));
}
var separators = Enumerable.Repeat(SyntaxFactory.Token(SyntaxKind.CommaToken), argSyntaxes.Count - 1);
return SyntaxFactory.SeparatedList(argSyntaxes, separators);
}
///
/// Transforms method calls into property and indexer accesses where appropriate.
///
/// The conversion context.
/// The MethodCallExpr to be transformed.
/// The resulting transformed syntax.
/// true if the syntax was transformed, false otherwise
public static bool TryTransformMethodCall(ConversionContext context, MethodCallExpr methodCallExpr,
out ExpressionSyntax? transformedSyntax)
{
if (methodCallExpr.getScope().FromOptional() is { } scope)
{
var methodName = methodCallExpr.getName();
var args = methodCallExpr.getArguments();
switch (methodName.getIdentifier())
{
case "size" when args.size() == 0:
var scopeSyntaxSize = ExpressionVisitor.VisitExpression(context, scope);
transformedSyntax = ReplaceSizeByCount(scopeSyntaxSize);
return true;
case "get" when args.size() == 1:
var scopeSyntaxGet = ExpressionVisitor.VisitExpression(context, scope);
if (scopeSyntaxGet is null)
{
transformedSyntax = null;
return false;
}
transformedSyntax = ReplaceGetByIndexAccess(context, scopeSyntaxGet, args);
return true;
case "set" when args.size() == 2:
var scopeSyntaxSet = ExpressionVisitor.VisitExpression(context, scope);
if (scopeSyntaxSet is null)
{
transformedSyntax = null;
return false;
}
transformedSyntax = ReplaceSetByIndexAccess(context, scopeSyntaxSet, args);
return true;
}
}
transformedSyntax = null;
return false;
static MemberAccessExpressionSyntax? ReplaceSizeByCount(ExpressionSyntax? scopeSyntax)
{
if (scopeSyntax is null)
{
return null;
}
// Replace expr.Size() by expr.Count
return SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
scopeSyntax,
SyntaxFactory.IdentifierName(SyntaxFactory.Identifier("Count")));
}
static ExpressionSyntax ReplaceGetByIndexAccess(ConversionContext context, ExpressionSyntax scopeSyntax,
java.util.List args)
{
// Replace expr.Get(i) by expr[i]
return SyntaxFactory.ElementAccessExpression(
scopeSyntax,
SyntaxFactory.BracketedArgumentList(GetSeparatedListFromArguments(context, args))
);
}
static ExpressionSyntax? ReplaceSetByIndexAccess(
ConversionContext context,
ExpressionSyntax scopeSyntax,
java.util.List args)
{
// Replace expr.Set(i,v) by expr[i] = v
var argsList = args.ToList() ?? new List();
var left = SyntaxFactory.ElementAccessExpression(
scopeSyntax,
SyntaxFactory.BracketedArgumentList(GetSeparatedListFromArguments(context, argsList.Take(1)))
).WithTrailingTrivia(SyntaxFactory.Whitespace(" "));
var right = ExpressionVisitor.VisitExpression(context, argsList[1])?.WithLeadingTrivia(SyntaxFactory.Whitespace(" "));
if (right is null)
{
return null;
}
return SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, left, right);
}
}
}