From 5bd807ca99e89cfe5ac631f410b6427d6c90791f Mon Sep 17 00:00:00 2001 From: Paul Irwin Date: Mon, 17 Aug 2026 14:01:00 -0600 Subject: [PATCH] Split Javadoc on the source file's line endings, not the host's Javadoc content was split with Environment.NewLine, but JavaParser's getContent() preserves the line endings of the parsed file, which are independent of the host OS. When the two differ -- most commonly a Unix-line-ending file converted on Windows -- the content stayed a single unsplit line, the per-line regex never matched, and the comment was dropped from the output entirely. Single-line /** ... */ comments were unaffected because they contain no newline to split on, which is why the bug looked specific to multi-line Javadoc. Add SplitLines(), which normalizes before splitting, and use it for the three places that split text originating from the Java source. FormatAsBlockComment already did this by hand and now shares the helper. Also normalize the raw comment trivia paths so comment content adopts the generated file's line endings rather than leaking the Java file's, matching the existing Whitespace.NewLine convention. The remaining Environment.NewLine uses are all on the output side and stay as they are. Add tests for both \n and \r\n inputs. These also cover the Javadoc to /// conversion, which previously had no test coverage at all, since every other suite sets IncludeComments to false. Fixes #97 Co-Authored-By: Claude Opus 5 (1M context) --- JavaToCSharp.Tests/CommentTests.cs | 82 ++++++++++++++++++++++++++++++ JavaToCSharp/CommentsHelper.cs | 24 ++++++--- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/JavaToCSharp.Tests/CommentTests.cs b/JavaToCSharp.Tests/CommentTests.cs index 4e1c3da..5f53ee6 100644 --- a/JavaToCSharp.Tests/CommentTests.cs +++ b/JavaToCSharp.Tests/CommentTests.cs @@ -241,4 +241,86 @@ public class Foo Assert.Equal(expected.ReplaceLineEndings(), parsed.ReplaceLineEndings()); } + + [Theory] + [InlineData("\n")] + [InlineData("\r\n")] + public void MultiLineJavadoc_ShouldConvertToXmlDoc_RegardlessOfLineEndings(string newLine) + { + // Issue #97: the Javadoc content was split on Environment.NewLine, so a Java file whose + // line endings differ from the host OS collapsed into a single unparsed line. + string javaCode = """ + package foo; + + public class Foo { + /** + * Really cool field that contains something. + * Keep in mind that this field is cooler than awesomeField. + */ + public int reallyAwesomeField; + } + """.ReplaceLineEndings(newLine); + + var options = new JavaConversionOptions(); + options.Usings.Clear(); + + var parsed = JavaToCSharpConverter.ConvertText(javaCode, options) ?? ""; + + testOutputHelper.WriteLine(parsed); + + const string expected = """ + namespace Foo + { + public class Foo + { + /// + /// Really cool field that contains something. + /// Keep in mind that this field is cooler than awesomeField. + /// + public int reallyAwesomeField; + } + } + """; + + Assert.Equal(expected.ReplaceLineEndings(), parsed.ReplaceLineEndings()); + } + + [Theory] + [InlineData("\n")] + [InlineData("\r\n")] + public void MultiLineJavadocWithTags_ShouldConvertToXmlDoc_RegardlessOfLineEndings(string newLine) + { + string javaCode = """ + package foo; + + public class Foo { + /** + * Does something useful. + * Second line of the summary. + * + * @param name the name to use + * @return the computed value + * @throws IllegalStateException if it breaks + */ + public int doIt(String name) { + return 1; + } + } + """.ReplaceLineEndings(newLine); + + var options = new JavaConversionOptions(); + options.Usings.Clear(); + + var parsed = JavaToCSharpConverter.ConvertText(javaCode, options) ?? ""; + + testOutputHelper.WriteLine(parsed); + + Assert.Contains("/// ", parsed); + Assert.Contains("/// Does something useful.", parsed); + Assert.Contains("/// Second line of the summary.", parsed); + Assert.Contains("/// ", parsed); + Assert.Contains("""/// the name to use""", parsed); + Assert.Contains("/// the computed value", parsed); + Assert.Contains("""/// if it breaks""", parsed); + } } diff --git a/JavaToCSharp/CommentsHelper.cs b/JavaToCSharp/CommentsHelper.cs index 31cafc9..e14d9e0 100644 --- a/JavaToCSharp/CommentsHelper.cs +++ b/JavaToCSharp/CommentsHelper.cs @@ -102,7 +102,7 @@ public static CompilationUnitSyntax AddPackageComments(CompilationUnitSyntax syn } else { - var commentTrivia = SyntaxFactory.SyntaxTrivia(kind, pre + comment.getContent() + post); + var commentTrivia = SyntaxFactory.SyntaxTrivia(kind, pre + NormalizeLineEndings(comment.getContent()) + post); if (pos == CommentPosition.Leading) { leadingTriviaList.Add(commentTrivia); @@ -136,13 +136,25 @@ private static SyntaxTrivia CreateNonMemberCommentTrivia(JavaComments.Comment co FormatAsBlockComment(comment.getContent()) + suffix); } - return SyntaxFactory.SyntaxTrivia(kind, pre + comment.getContent() + post + suffix); + return SyntaxFactory.SyntaxTrivia(kind, pre + NormalizeLineEndings(comment.getContent()) + post + suffix); } + /// + /// Splits text that originated from the Java source into lines. The line endings of the parsed file are + /// preserved by JavaParser and are independent of the host OS, so splitting on + /// would fail whenever the two differ (see issue #97). + /// + private static string[] SplitLines(string text) => text.ReplaceLineEndings("\n").Split('\n'); + + /// + /// Rewrites line endings that came from the Java source to the ones used for the generated C# output, so that + /// multi-line comments do not introduce line endings foreign to the rest of the file. + /// + private static string NormalizeLineEndings(string text) => text.ReplaceLineEndings(Environment.NewLine); + private static string FormatAsBlockComment(string content) { - var lines = content.ReplaceLineEndings("\n") - .Split('\n') + var lines = SplitLines(content) .Select(line => line.TrimStart().TrimStart('*').Trim()) .ToList(); @@ -293,7 +305,7 @@ public static IEnumerable ConvertToComment(IEnumerable(); foreach (var code in codes) { - string[] input = code.ToString().Split([Environment.NewLine], StringSplitOptions.None); + string[] input = SplitLines(code.ToString()); outputs.AddRange(input); } @@ -318,7 +330,7 @@ public static IEnumerable ConvertToComment(IEnumerable ConvertDocComment(JavaComments.Comment comment, string? post) { - string[] input = comment.getContent().Split([Environment.NewLine], StringSplitOptions.None); + string[] input = SplitLines(comment.getContent()); var output = new List(); var remarks = new List(); // For Java tags unknown in C# var currentOutput = output;