Skip to content

Commit 28d4d14

Browse files
authored
Support (almost) all options in the CLI app, paulirwin#58 (paulirwin#77)
* WIP on adding System.Commandline to the CLI, paulirwin#58 * Support all options (except package translations) in the CLI, paulirwin#58
1 parent 76b529c commit 28d4d14

File tree

2 files changed

+205
-56
lines changed

2 files changed

+205
-56
lines changed

JavaToCSharpCli/JavaToCSharpCli.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
</PropertyGroup>
1111
<ItemGroup>
1212
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
13+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
1314
</ItemGroup>
1415
<ItemGroup>
1516
<ProjectReference Include="..\JavaToCSharp\JavaToCSharp.csproj" />

JavaToCSharpCli/Program.cs

Lines changed: 204 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,259 @@
1-
using JavaToCSharp;
1+
using System.CommandLine;
2+
using System.CommandLine.Invocation;
3+
using JavaToCSharp;
24
using Microsoft.Extensions.Logging;
35

46
namespace JavaToCSharpCli;
57

8+
/// <summary>
9+
/// The main JavaToCSharpCli program class.
10+
/// </summary>
611
public class Program
712
{
13+
private static readonly ILoggerFactory _loggerFactory;
814
private static readonly ILogger _logger;
915

16+
private static readonly Option<bool> _includeUsingsOption = new(
17+
name: "--include-usings",
18+
description: "Include using directives in output",
19+
getDefaultValue: () => true);
20+
21+
private static readonly Option<bool> _includeNamespaceOption = new(
22+
name: "--include-namespace",
23+
description: "Include namespace in output",
24+
getDefaultValue: () => true);
25+
26+
private static readonly Option<bool> _includeCommentsOption = new(
27+
name: "--include-comments",
28+
description: "Include comments in output",
29+
getDefaultValue: () => true);
30+
31+
private static readonly Option<bool> _useDebugAssertOption = new(
32+
name: "--use-debug-assert",
33+
description: "Use Debug.Assert for asserts",
34+
getDefaultValue: () => false);
35+
36+
private static readonly Option<bool> _startInterfaceNamesWithIOption = new(
37+
name: "--start-interface-names-with-i",
38+
description: "Prefix interface names with the letter I",
39+
getDefaultValue: () => true);
40+
41+
private static readonly Option<bool> _commentUnrecognizedCodeOption = new(
42+
name: "--comment-unrecognized-code",
43+
description: "Include unrecognized code in output as commented-out code",
44+
getDefaultValue: () => true);
45+
46+
private static readonly Option<bool> _systemOutToConsoleOption = new(
47+
name: "--system-out-to-console",
48+
description: "Convert System.out calls to Console",
49+
getDefaultValue: () => false);
50+
51+
private static readonly Option<bool> _clearDefaultUsingsOption = new(
52+
name: "--clear-usings",
53+
description: "Remove all default usings provided by this app",
54+
getDefaultValue: () => false);
55+
56+
private static readonly Option<List<string>> _addUsingsOption = new(
57+
name: "--add-using",
58+
description: "Adds a using directive to the collection of usings")
59+
{
60+
ArgumentHelpName = "namespace"
61+
};
62+
1063
static Program()
1164
{
12-
var loggerFactory =
13-
LoggerFactory.Create(builder => builder.AddSimpleConsole().SetMinimumLevel(LogLevel.Information));
14-
_logger = loggerFactory.CreateLogger<Program>();
65+
_loggerFactory = LoggerFactory.Create(builder =>
66+
builder.AddSimpleConsole().SetMinimumLevel(LogLevel.Information));
67+
_logger = _loggerFactory.CreateLogger<Program>();
1568
}
1669

17-
public static void Main(string[] args)
70+
public static async Task Main(string[] args)
1871
{
19-
try
72+
var rootCommand = new RootCommand("Java to C# Converter")
2073
{
21-
if (args.Length < 3)
22-
ShowHelp();
23-
else
24-
switch (args[0])
25-
{
26-
case "-f":
27-
ConvertToCSharpFile(args[1], args[2]);
28-
break;
29-
case "-d":
30-
ConvertToCSharpDir(args[1], args[2]);
31-
break;
32-
default:
33-
ShowHelp();
34-
break;
35-
}
74+
Description = "A syntactic transformer of source code from Java to C#."
75+
};
76+
77+
rootCommand.AddCommand(CreateFileCommand());
78+
rootCommand.AddCommand(CreateDirectoryCommand());
79+
80+
rootCommand.AddGlobalOption(_includeUsingsOption);
81+
rootCommand.AddGlobalOption(_includeNamespaceOption);
82+
rootCommand.AddGlobalOption(_includeCommentsOption);
83+
rootCommand.AddGlobalOption(_useDebugAssertOption);
84+
rootCommand.AddGlobalOption(_startInterfaceNamesWithIOption);
85+
rootCommand.AddGlobalOption(_commentUnrecognizedCodeOption);
86+
rootCommand.AddGlobalOption(_systemOutToConsoleOption);
87+
rootCommand.AddGlobalOption(_clearDefaultUsingsOption);
88+
rootCommand.AddGlobalOption(_addUsingsOption);
89+
90+
await rootCommand.InvokeAsync(args);
91+
92+
// flush logs
93+
_loggerFactory.Dispose();
94+
}
95+
96+
private static Command CreateFileCommand()
97+
{
98+
var inputArgument = new Argument<FileInfo>(
99+
name: "input",
100+
description: "A Java source code file to convert");
101+
102+
var outputArgument = new Argument<FileInfo?>(
103+
name: "output",
104+
description: "Path to place the C# output file, or stdout if omitted",
105+
getDefaultValue: () => null);
106+
107+
var fileCommand = new Command("file", "Convert a Java file to C#");
108+
fileCommand.AddArgument(inputArgument);
109+
fileCommand.AddArgument(outputArgument);
110+
111+
fileCommand.SetHandler(context =>
112+
{
113+
var input = context.ParseResult.GetValueForArgument(inputArgument);
114+
var output = context.ParseResult.GetValueForArgument(outputArgument);
115+
116+
var options = GetJavaConversionOptions(context);
117+
118+
ConvertToCSharpFile(input, output, options);
119+
});
120+
121+
return fileCommand;
122+
}
123+
124+
private static JavaConversionOptions GetJavaConversionOptions(InvocationContext context)
125+
{
126+
var options = new JavaConversionOptions
127+
{
128+
IncludeUsings = context.ParseResult.GetValueForOption(_includeUsingsOption),
129+
IncludeComments = context.ParseResult.GetValueForOption(_includeCommentsOption),
130+
IncludeNamespace = context.ParseResult.GetValueForOption(_includeNamespaceOption),
131+
ConvertSystemOutToConsole = context.ParseResult.GetValueForOption(_systemOutToConsoleOption),
132+
StartInterfaceNamesWithI = context.ParseResult.GetValueForOption(_startInterfaceNamesWithIOption),
133+
UseDebugAssertForAsserts = context.ParseResult.GetValueForOption(_useDebugAssertOption),
134+
UseUnrecognizedCodeToComment = context.ParseResult.GetValueForOption(_commentUnrecognizedCodeOption)
135+
};
136+
137+
if (context.ParseResult.GetValueForOption(_clearDefaultUsingsOption))
138+
{
139+
options.ClearUsings();
36140
}
37-
catch (Exception ex)
141+
142+
foreach (string ns in context.ParseResult.GetValueForOption(_addUsingsOption) ?? new List<string>())
38143
{
39-
_logger.LogError(ex, "Error: {Message}", ex.Message);
144+
options.AddUsing(ns);
40145
}
146+
147+
return options;
148+
}
149+
150+
private static Command CreateDirectoryCommand()
151+
{
152+
var inputArgument = new Argument<DirectoryInfo>(
153+
name: "input",
154+
description: "A directory containing Java source code files to convert");
155+
156+
var outputArgument = new Argument<DirectoryInfo>(
157+
name: "output",
158+
description: "Path to place the C# output files");
159+
160+
var dirCommand = new Command("dir", "Convert a directory containing Java files to C#");
161+
dirCommand.AddArgument(inputArgument);
162+
dirCommand.AddArgument(outputArgument);
41163

42-
// allow logger background thread to flush
43-
Thread.Sleep(TimeSpan.FromSeconds(1));
164+
dirCommand.SetHandler(context =>
165+
{
166+
var input = context.ParseResult.GetValueForArgument(inputArgument);
167+
var output = context.ParseResult.GetValueForArgument(outputArgument);
168+
169+
var options = GetJavaConversionOptions(context);
170+
171+
ConvertToCSharpDir(input, output, options);
172+
});
173+
174+
return dirCommand;
44175
}
45176

46-
private static void ConvertToCSharpDir(string folderPath, string outputFolderPath)
177+
private static void ConvertToCSharpDir(DirectoryInfo inputDirectory, DirectoryInfo outputDirectory, JavaConversionOptions options)
47178
{
48-
if (Directory.Exists(folderPath))
179+
if (inputDirectory.Exists)
49180
{
50-
var input = new DirectoryInfo(folderPath);
51-
foreach (var f in input.GetFiles("*.java", SearchOption.AllDirectories))
181+
foreach (var f in inputDirectory.GetFiles("*.java", SearchOption.AllDirectories))
52182
{
53183
string? directoryName = f.DirectoryName;
54184
if (string.IsNullOrWhiteSpace(directoryName))
55185
{
56186
continue;
57187
}
58-
59-
string newFolderPath = directoryName.Replace(folderPath, outputFolderPath, StringComparison.OrdinalIgnoreCase);
60-
if (!Directory.Exists(newFolderPath))
188+
189+
if (!outputDirectory.Exists)
61190
{
62-
Directory.CreateDirectory(newFolderPath);
191+
outputDirectory.Create();
63192
}
64193

65-
ConvertToCSharpFile(f.FullName,
66-
Path.Combine(newFolderPath, Path.ChangeExtension(f.Name, ".cs")),
67-
false);
194+
ConvertToCSharpFile(f,
195+
new FileInfo(Path.Combine(outputDirectory.FullName, Path.ChangeExtension(f.Name, ".cs"))),
196+
options,
197+
false);
68198
}
69199
}
70200
else
71-
_logger.LogError("Java input folder {path} doesn't exist!", folderPath);
201+
_logger.LogError("Java input folder {path} doesn't exist!", inputDirectory);
72202
}
73203

74-
private static void ConvertToCSharpFile(string filePath, string outputFilePath, bool overwrite = true)
204+
private static void ConvertToCSharpFile(FileSystemInfo inputFile, FileSystemInfo? outputFile, JavaConversionOptions options, bool overwrite = true)
75205
{
76-
if (!overwrite && File.Exists(outputFilePath))
77-
_logger.LogInformation("{outputFilePath} exists, skip to next.", outputFilePath);
78-
else if (File.Exists(filePath))
206+
if (!overwrite && outputFile is { Exists: true })
207+
_logger.LogInformation("{outputFilePath} exists, skip to next.", outputFile);
208+
else if (inputFile.Exists)
79209
{
80210
try
81211
{
82-
string javaText = File.ReadAllText(filePath);
83-
var options = new JavaConversionOptions();
212+
string javaText = File.ReadAllText(inputFile.FullName);
84213

85214
options.WarningEncountered += (_, eventArgs) =>
86-
{
87-
_logger.LogWarning("Line {JavaLineNumber}: {Message}", eventArgs.JavaLineNumber, eventArgs.Message);
88-
File.AppendAllText(Path.ChangeExtension(outputFilePath, ".warning"), eventArgs.Message + Environment.NewLine);
89-
};
215+
{
216+
if (outputFile != null)
217+
{
218+
_logger.LogWarning("Line {JavaLineNumber}: {Message}", eventArgs.JavaLineNumber,
219+
eventArgs.Message);
220+
}
221+
222+
OutputFileOrPrint(outputFile != null ? Path.ChangeExtension(outputFile.FullName, ".warning") : null,
223+
eventArgs.Message + Environment.NewLine);
224+
};
90225

91226
string? parsed = JavaToCSharpConverter.ConvertText(javaText, options);
92-
File.WriteAllText(outputFilePath, parsed);
93-
_logger.LogInformation("{filePath} converted!", filePath);
227+
OutputFileOrPrint(outputFile?.FullName, parsed ?? string.Empty);
228+
229+
if (outputFile != null)
230+
{
231+
_logger.LogInformation("{filePath} converted!", inputFile.Name);
232+
}
94233
}
95234
catch (Exception ex)
96235
{
97-
_logger.LogError("{filePath} failed! {type}: {message}", filePath, ex.GetType().Name, ex);
98-
File.WriteAllText(Path.ChangeExtension(outputFilePath, ".error"), ex.ToString());
236+
_logger.LogError("{filePath} failed! {type}: {message}", inputFile.Name, ex.GetType().Name, ex);
237+
238+
if (outputFile != null)
239+
{
240+
File.WriteAllText(Path.ChangeExtension(outputFile.FullName, ".error"), ex.ToString());
241+
}
99242
}
100243
}
101244
else
102-
_logger.LogError("Java input file {filePath} doesn't exist!", filePath);
245+
_logger.LogError("Java input file {filePath} doesn't exist!", inputFile.FullName);
103246
}
104247

105-
private static void ShowHelp()
248+
private static void OutputFileOrPrint(string? fileName, string contents)
106249
{
107-
Console.WriteLine("Usage:");
108-
Console.WriteLine("\tJavaToCSharpCli.exe -f [pathToJavaFile] [pathToCsOutputFile]");
109-
Console.WriteLine("\tJavaToCSharpCli.exe -d [pathToJavaFolder] [pathToCsOutputFolder]");
250+
if (fileName == null)
251+
{
252+
Console.Out.WriteLine(contents);
253+
}
254+
else
255+
{
256+
File.WriteAllText(fileName, contents);
257+
}
110258
}
111259
}

0 commit comments

Comments
 (0)