diff --git a/pkgs/jnigen/CHANGELOG.md b/pkgs/jnigen/CHANGELOG.md index d19fc083a6..115b09cb6a 100644 --- a/pkgs/jnigen/CHANGELOG.md +++ b/pkgs/jnigen/CHANGELOG.md @@ -17,6 +17,8 @@ elements.dart is no longer exported from the library, as these classes were always intended to be private. `Config.importedClasses` and `Config.importClasses()` have also been made private. +- Preserve Java `@deprecated` Javadoc messages in generated Dart + `@Deprecated` annotations. ## 0.16.0 diff --git a/pkgs/jnigen/lib/src/bindings/dart_generator.dart b/pkgs/jnigen/lib/src/bindings/dart_generator.dart index 49ad70611d..869813a568 100644 --- a/pkgs/jnigen/lib/src/bindings/dart_generator.dart +++ b/pkgs/jnigen/lib/src/bindings/dart_generator.dart @@ -1312,7 +1312,9 @@ ${modifier}final _$idName = $_protectedExtension } final params = defArgs.delimited(', '); if (node.isDeprecated) { - s.writeln(" @core\$_.Deprecated('This Java method is deprecated.')"); + final message = node.javadoc?.deprecatedMessage ?? + "'This Java method is deprecated.'"; + s.writeln(' @core\$_.Deprecated($message)'); } if (node.methodKind == MethodKind.getter) { s.write(' $ifStatic$returnType get $name '); diff --git a/pkgs/jnigen/lib/src/elements/elements.dart b/pkgs/jnigen/lib/src/elements/elements.dart index 443db2002d..1524f8f517 100644 --- a/pkgs/jnigen/lib/src/elements/elements.dart +++ b/pkgs/jnigen/lib/src/elements/elements.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:convert'; + import 'package:json_annotation/json_annotation.dart'; import 'package:meta/meta.dart'; @@ -973,6 +975,48 @@ class JavaDocComment implements Element { final String comment; + String? get deprecatedMessage { + final lines = comment.split('\n'); + final messageLines = []; + var readingDeprecatedTag = false; + + for (final line in lines) { + final trimmed = line.trim(); + + if (!readingDeprecatedTag) { + if (trimmed.startsWith('@deprecated')) { + readingDeprecatedTag = true; + + final firstLine = trimmed.substring('@deprecated'.length).trim(); + if (firstLine.isNotEmpty) { + messageLines.add(firstLine); + } + } + continue; + } + + // A new Javadoc block tag marks the end of the deprecated message. + if (trimmed.startsWith('@')) { + break; + } + + if (trimmed.isNotEmpty) { + messageLines.add(trimmed); + } + } + + final message = messageLines.join('\n').trim(); + if (message.isEmpty) return null; + + final encoded = jsonEncode(message); + final contents = encoded + .substring(1, encoded.length - 1) + .replaceAll("'", r"\'") + .replaceAll(r'$', r'\$'); + + return "'$contents'"; + } + factory JavaDocComment.fromJson(Map json) => _$JavaDocCommentFromJson(json); diff --git a/pkgs/jnigen/test/deprecated_message_test.dart b/pkgs/jnigen/test/deprecated_message_test.dart new file mode 100644 index 0000000000..02e3476a16 --- /dev/null +++ b/pkgs/jnigen/test/deprecated_message_test.dart @@ -0,0 +1,114 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:jnigen/src/elements/elements.dart'; +import 'package:test/test.dart'; + +void main() { + group('JavaDocComment.deprecatedMessage', () { + test('extracts a single-line message', () { + final comment = JavaDocComment( + comment: '@deprecated Use the replacement method instead.', + ); + + expect( + comment.deprecatedMessage, + "'Use the replacement method instead.'", + ); + }); + + test('allows description text before the tag', () { + final comment = JavaDocComment( + comment: ''' +This method is retained for compatibility. + +@deprecated Use the replacement method instead. +''', + ); + + expect( + comment.deprecatedMessage, + "'Use the replacement method instead.'", + ); + }); + + test('extracts a multiline message', () { + final comment = JavaDocComment( + comment: ''' +@deprecated Use the replacement method instead. +This method will be removed in a future release. +''', + ); + + expect( + comment.deprecatedMessage, + r"'Use the replacement method instead.\nThis method will be removed in a future release.'", + ); + }); + + test('stops at the next block tag', () { + final comment = JavaDocComment( + comment: ''' +@deprecated Use the replacement method instead. +This method will be removed soon. +@return the old result +''', + ); + + expect( + comment.deprecatedMessage, + r"'Use the replacement method instead.\nThis method will be removed soon.'", + ); + }); + + test('returns null when the tag is absent', () { + final comment = JavaDocComment( + comment: 'This is a regular Javadoc comment.', + ); + + expect(comment.deprecatedMessage, isNull); + }); + + test('returns null when the message is empty', () { + final comment = JavaDocComment( + comment: '@deprecated', + ); + + expect(comment.deprecatedMessage, isNull); + }); + + test('escapes apostrophes and dollar signs', () { + final comment = JavaDocComment( + comment: r"@deprecated Don't use $oldMethod.", + ); + + expect( + comment.deprecatedMessage, + r"'Don\'t use \$oldMethod.'", + ); + }); + + test('escapes backslashes', () { + final comment = JavaDocComment( + comment: r'@deprecated Use C:\temp instead.', + ); + + expect( + comment.deprecatedMessage, + r"'Use C:\\temp instead.'", + ); + }); + + test('escapes double quotes', () { + final comment = JavaDocComment( + comment: '@deprecated Use "newMethod" instead.', + ); + + expect( + comment.deprecatedMessage, + "'Use \\\"newMethod\\\" instead.'", + ); + }); + }); +} diff --git a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java index 18ff339ff8..e8278aa15a 100644 --- a/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java +++ b/pkgs/jnigen/test/simple_package_test/java/com/github/dart_lang/jnigen/simple_package/Example.java @@ -147,6 +147,11 @@ public String joinStrings(List values, String delim) { return null; } + /** + * This method is retained for compatibility. + * + * @deprecated Use methodWithSeveralParams instead. + */ @Deprecated public String deprecatedMethod() { return "deprecated";