Skip to content

Commit f7d068c

Browse files
authored
chore(java): add a method to copy a java method and rename it (#1162)
chore: add method to deprecate java methods
1 parent 82fe6d9 commit f7d068c

6 files changed

Lines changed: 430 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ venv.bak/
110110
.vscode/
111111
.settings/
112112
.project
113+
.idea/
113114

114115
# synthtool artifacts
115116
working_repo/

synthtool/languages/java.py

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,252 @@ class Example {
572572
for line in lines:
573573
# print(line)
574574
fp.write(line)
575+
576+
577+
def copy_and_rename_method(filename: str, signature: str, before: str, after: str):
578+
"""Helper to make a copy an entire method and rename it.
579+
580+
Goes line-by-line to detect the start of the block. Determines
581+
the end of the block by a closing brace at the same indentation
582+
level. This requires the file to be correctly formatted.
583+
The method is copied over and renamed in the method signature.
584+
The calls to both methods are separate and unaffected.
585+
586+
Example: consider the following class:
587+
588+
class Example {
589+
public void main(String[] args) {
590+
System.out.println("Hello World");
591+
}
592+
593+
public String foo() {
594+
return "bar";
595+
}
596+
}
597+
598+
To copy and rename the `main` method above, use:
599+
600+
copy_and_rename_method('path/to/file', 'public void main(String[] args)',
601+
'main', 'foo1')
602+
603+
Args:
604+
filename (str): Path to source file
605+
signature (str): Full signature of the method to remove. Example:
606+
`public void main(String[] args)`.
607+
before (str): name of the method to be copied
608+
after (str): new name of the copied method
609+
"""
610+
lines = []
611+
method = []
612+
leading_regex = None
613+
with open(filename, "r") as fp:
614+
line = fp.readline()
615+
while line:
616+
# for each line, try to find the matching
617+
regex = re.compile("(\\s*)" + re.escape(signature) + ".*")
618+
match = regex.match(line)
619+
if match:
620+
leading_regex = re.compile(match.group(1) + "}")
621+
lines.append(line)
622+
method.append(line.replace(before, after))
623+
line = fp.readline()
624+
continue
625+
626+
lines.append(line)
627+
# not in a ignore block - preserve the line
628+
if leading_regex:
629+
method.append(line)
630+
else:
631+
line = fp.readline()
632+
continue
633+
634+
# detect the closing tag based on the leading spaces
635+
match = leading_regex.match(line)
636+
if match:
637+
# block is closed, resume capturing content
638+
leading_regex = None
639+
lines.append("\n")
640+
lines.extend(method)
641+
642+
line = fp.readline()
643+
644+
with open(filename, "w") as fp:
645+
for line in lines:
646+
# print(line)
647+
fp.write(line)
648+
649+
650+
def add_javadoc(filename: str, signature: str, javadoc_type: str, content: List[str]):
651+
"""Helper to add a javadoc annoatation to a method.
652+
653+
Goes line-by-line to detect the start of the block.
654+
Then finds the existing method comment (if it exists). If the
655+
comment already exists, it will append the javadoc annotation
656+
to the javadoc block. Otherwise, it will create a new javadoc
657+
comment block.
658+
659+
Example: consider the following class:
660+
661+
class Example {
662+
public void main(String[] args) {
663+
System.out.println("Hello World");
664+
}
665+
666+
public String foo() {
667+
return "bar";
668+
}
669+
}
670+
671+
To add a javadoc annotation the `main` method above, use:
672+
673+
add_javadoc('path/to/file', 'public void main(String[] args)',
674+
'deprecated', 'Please use foo instead.')
675+
676+
Args:
677+
filename (str): Path to source file
678+
signature (str): Full signature of the method to remove. Example:
679+
`public void main(String[] args)`.
680+
javadoc_type (str): The type of javadoc annotation. Example: `deprecated`.
681+
content (List[str]): The javadoc lines
682+
"""
683+
lines: List[str] = []
684+
annotations: List[str] = []
685+
with open(filename, "r") as fp:
686+
line = fp.readline()
687+
while line:
688+
# for each line, try to find the matching
689+
regex = re.compile("(\\s*)" + re.escape(signature) + ".*")
690+
match = regex.match(line)
691+
if match:
692+
leading_spaces = len(line) - len(line.lstrip())
693+
indent = leading_spaces * " "
694+
last_line = lines.pop()
695+
while last_line.lstrip() and last_line.lstrip()[0] == "@":
696+
annotations.append(last_line)
697+
last_line = lines.pop()
698+
if last_line.strip() == "*/":
699+
first = True
700+
for content_line in content:
701+
if first:
702+
lines.append(
703+
indent
704+
+ " * @"
705+
+ javadoc_type
706+
+ " "
707+
+ content_line
708+
+ "\n"
709+
)
710+
first = False
711+
else:
712+
lines.append(indent + " * " + content_line + "\n")
713+
lines.append(last_line)
714+
else:
715+
lines.append(last_line)
716+
lines.append(indent + "/**\n")
717+
first = True
718+
for content_line in content:
719+
if first:
720+
lines.append(
721+
indent
722+
+ " * @"
723+
+ javadoc_type
724+
+ " "
725+
+ content_line
726+
+ "\n"
727+
)
728+
first = False
729+
else:
730+
lines.append(indent + " * " + content_line + "\n")
731+
lines.append(indent + " */\n")
732+
lines.extend(annotations[::-1])
733+
lines.append(line)
734+
line = fp.readline()
735+
736+
with open(filename, "w") as fp:
737+
for line in lines:
738+
# print(line)
739+
fp.write(line)
740+
741+
742+
def annotate_method(filename: str, signature: str, annotation: str):
743+
"""Helper to add an annotation to a method.
744+
745+
Goes line-by-line to detect the start of the block.
746+
Then adds the annotation above the found method signature.
747+
748+
Example: consider the following class:
749+
750+
class Example {
751+
public void main(String[] args) {
752+
System.out.println("Hello World");
753+
}
754+
755+
public String foo() {
756+
return "bar";
757+
}
758+
}
759+
760+
To add an annotation the `main` method above, use:
761+
762+
annotate_method('path/to/file', 'public void main(String[] args)',
763+
'@Generated()')
764+
765+
Args:
766+
filename (str): Path to source file
767+
signature (str): Full signature of the method to remove. Example:
768+
`public void main(String[] args)`.
769+
annotation (str): Full annotation. Example: `@Deprecated`
770+
"""
771+
lines: List[str] = []
772+
with open(filename, "r") as fp:
773+
line = fp.readline()
774+
while line:
775+
# for each line, try to find the matching
776+
regex = re.compile("(\\s*)" + re.escape(signature) + ".*")
777+
match = regex.match(line)
778+
if match:
779+
leading_spaces = len(line) - len(line.lstrip())
780+
indent = leading_spaces * " "
781+
lines.append(indent + annotation + "\n")
782+
lines.append(line)
783+
line = fp.readline()
784+
785+
with open(filename, "w") as fp:
786+
for line in lines:
787+
# print(line)
788+
fp.write(line)
789+
790+
791+
def deprecate_method(filename: str, signature: str, alternative: str):
792+
"""Helper to deprecate a method.
793+
794+
Goes line-by-line to detect the start of the block.
795+
Then adds the deprecation comment before the method signature.
796+
The @Deprecation annotation is also added.
797+
798+
Example: consider the following class:
799+
800+
class Example {
801+
public void main(String[] args) {
802+
System.out.println("Hello World");
803+
}
804+
805+
public String foo() {
806+
return "bar";
807+
}
808+
}
809+
810+
To deprecate the `main` method above, use:
811+
812+
deprecate_method('path/to/file', 'public void main(String[] args)',
813+
DEPRECATION_WARNING.format(new_method="foo"))
814+
815+
Args:
816+
filename (str): Path to source file
817+
signature (str): Full signature of the method to remove. Example:
818+
`public void main(String[] args)`.
819+
alternative: DEPRECATION WARNING: multiline javadoc comment with user
820+
specified leading open/close comment tags
821+
"""
822+
add_javadoc(filename, signature, "deprecated", alternative.splitlines())
823+
annotate_method(filename, signature, "@Deprecated")

tests/test_language_java.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,57 @@ def test_remove_method():
120120
)
121121

122122

123+
def test_copy_and_rename_method():
124+
with tempfile.TemporaryDirectory() as tempdir:
125+
shutil.copyfile(
126+
"tests/testdata/SampleClass.java", tempdir + "/SampleClass.java"
127+
)
128+
129+
java.copy_and_rename_method(
130+
tempdir + "/SampleClass.java", "public static void foo()", "foo", "foobar"
131+
)
132+
java.copy_and_rename_method(
133+
tempdir + "/SampleClass.java", "public void asdf()", "asdf", "xyz"
134+
)
135+
assert_matches_golden(
136+
"tests/testdata/SampleCopyMethodGolden.java", tempdir + "/SampleClass.java"
137+
)
138+
139+
140+
def test_deprecate_method():
141+
# with tempfile.TemporaryDirectory() as tempdir:
142+
if True:
143+
tempdir = tempfile.mkdtemp()
144+
shutil.copyfile(
145+
"tests/testdata/SampleDeprecateClass.java",
146+
tempdir + "/SampleDeprecateClass.java",
147+
)
148+
DEPRECATION_WARNING = """This method will be removed in the next major version.\nUse {{@link #{new_method}()}} instead"""
149+
ADDITIONAL_COMMENT = """{new_method} has the same functionality as foobar."""
150+
java.deprecate_method(
151+
tempdir + "/SampleDeprecateClass.java",
152+
"public void foo(String bar)",
153+
DEPRECATION_WARNING.format(new_method="sample"),
154+
)
155+
156+
# adding a comment when a javadoc and annotation already exists
157+
java.deprecate_method(
158+
tempdir + "/SampleDeprecateClass.java",
159+
"public void bar(String bar)",
160+
DEPRECATION_WARNING.format(new_method="sample"),
161+
)
162+
java.deprecate_method(
163+
tempdir + "/SampleDeprecateClass.java",
164+
"public void cat(String bar)",
165+
ADDITIONAL_COMMENT.format(new_method="sample"),
166+
)
167+
168+
assert_matches_golden(
169+
"tests/testdata/SampleDeprecateMethodGolden.java",
170+
tempdir + "/SampleDeprecateClass.java",
171+
)
172+
173+
123174
def test_fix_proto_license():
124175
with tempfile.TemporaryDirectory() as tempdir:
125176
temppath = Path(tempdir).resolve()
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package dlp;
18+
19+
class ExampleClass {
20+
public static void foo() {
21+
System.out.println("bar");
22+
}
23+
24+
public static void foobar() {
25+
System.out.println("bar");
26+
}
27+
28+
public static class InnerClass {
29+
public void asdf() {
30+
System.out.println("qwer");
31+
}
32+
33+
public void xyz() {
34+
System.out.println("qwer");
35+
}
36+
}
37+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
class ExampleClass {
18+
public void cat(String bar) {
19+
for (int i = 0; i < 3; i++) {
20+
System.out.println("this is a test " + bar);
21+
}
22+
}
23+
24+
@Beta
25+
@Generated()
26+
public void foo(String bar) {
27+
for (int i = 0; i < 3; i++) {
28+
System.out.println("this is a test " + bar);
29+
}
30+
}
31+
32+
/**
33+
* This is an existing comment.
34+
*/
35+
public void bar(String bar) {
36+
for (int i = 0; i < 3; i++) {
37+
System.out.println("this is a test " + bar);
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)