-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0c6fbbd
Showing
6 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# multiDeclRefactor | ||
|
||
Simple and a bit ugly tool for refactoring multiple declarations in Java: | ||
|
||
e.g. Given a java file has multiple declarations: | ||
|
||
```java | ||
public class MultiDecl { | ||
private int a, b, c; | ||
|
||
public List<String> d = new ArrayList<>(), | ||
e, | ||
f; | ||
} | ||
``` | ||
|
||
it will refactor all multiple declarations in one statements to each declaration per statement: | ||
```java | ||
public class MultiDecl { | ||
private int a; | ||
private int b; | ||
private int c; | ||
|
||
public List<String> d = new ArrayList<>(); | ||
public List<String> e; | ||
public List<String> f; | ||
} | ||
``` | ||
|
||
**Note** : this tool behaviour as a pre-processor for code analysis, so that the produced result is a bit unfriendly to human-readability: | ||
|
||
1. it will remove all comments appeared inside a statement of multiple declarations | ||
|
||
2. it will not keep the origin identation of a refactor statements | ||
|
||
## usage | ||
|
||
```bash | ||
./run-refactor.sh a.java b.java ... | ||
``` | ||
|
||
## dependency | ||
|
||
This tool depends on a hacked version of [checkstyle](https://github.com/CharlesZ-Chen/checkstyle) in my git repo. | ||
|
||
The hacked version of `checkstyle` is the backend of this tool, i.e. it detects the multiple declarations in a source file and then propage the diagnostic result to this tool. | ||
|
||
I currently still not have time to write build system and test framework for this tool, but hopefully I will create one soon. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?xml version="1.0"?> | ||
<!DOCTYPE module PUBLIC | ||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN" | ||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd"> | ||
|
||
<!-- | ||
Checkstyle configuration that checks the Google coding conventions from Google Java Style | ||
that can be found at https://google.github.io/styleguide/javaguide.html. | ||
Checkstyle is very configurable. Be sure to read the documentation at | ||
http://checkstyle.sf.net (or in your downloaded distribution). | ||
To completely disable a check, just comment it out or delete it from the file. | ||
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov. | ||
--> | ||
|
||
<module name = "Checker"> | ||
<property name="charset" value="UTF-8"/> | ||
|
||
<property name="severity" value="warning"/> | ||
|
||
<property name="fileExtensions" value="java, properties, xml"/> | ||
|
||
<module name="TreeWalker"> | ||
<module name="HackedMultipleVariableDeclarationsCheck"> | ||
<message key="multiple.variable.declarations" value=""/> | ||
<message key="multiple.variable.declarations.comma" value=""/> | ||
</module> | ||
</module> | ||
</module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import sys, os | ||
import shutil, tempfile | ||
import json | ||
|
||
REFACTOR_ISSUE_NAME = "HackedMultipleVariableDeclarations" | ||
|
||
def main(): | ||
try: | ||
data = json.load(sys.stdin) | ||
total_multi_decls = data[REFACTOR_ISSUE_NAME] | ||
file_based_multi_decls = collect_by_file(total_multi_decls) | ||
for refactor_file, multi_decls in file_based_multi_decls.iteritems(): | ||
refactor_decls(refactor_file, multi_decls) | ||
except ValueError: | ||
for line in sys.stdin: | ||
print line | ||
sys.exit(1) | ||
|
||
def refactor_decls(refactor_file, multi_decls): | ||
# print multi_decls | ||
backup = refactor_file + ".origin" | ||
shutil.copyfile(refactor_file, backup) | ||
origin_line_no = 1; | ||
with open(backup, 'r') as origin_file: | ||
with tempfile.NamedTemporaryFile(mode='w', suffix='.java') as out_file: | ||
ORIGIN_LINES = origin_file.readlines() | ||
refactor_list = produce_refactor_list(multi_decls, ORIGIN_LINES) | ||
|
||
# print refactor_list | ||
## ASSUMPTION: refactors are sorted by start line (first element in a tuple element in the list) | ||
for (start_line, end_line, decl_type, decls) in refactor_list: | ||
while origin_line_no < start_line: | ||
out_file.write(ORIGIN_LINES[origin_line_no - 1]) | ||
origin_line_no += 1; | ||
origin_line_no = end_line + 1 | ||
|
||
decl_list = list() | ||
for decl in decls: | ||
decl_list.append(decl_type + ' ' + decl + ';') | ||
|
||
for refactor_decl in decl_list: | ||
out_file.write(refactor_decl + '\n') | ||
|
||
|
||
while origin_line_no <= len(ORIGIN_LINES): | ||
out_file.write(ORIGIN_LINES[origin_line_no - 1]) | ||
origin_line_no += 1; | ||
|
||
out_file.flush() | ||
shutil.copyfile(out_file.name, refactor_file) | ||
|
||
def produce_refactor_list(multi_decls, ORIGIN_LINES): | ||
refactor_list = list() | ||
for multi_decl in multi_decls: | ||
refactor = produce_refactor(multi_decl, ORIGIN_LINES) | ||
refactor_list.append(refactor) | ||
return refactor_list | ||
|
||
def produce_refactor(multi_decl, ORIGIN_LINES): | ||
start_line_no = multi_decl['line'] | ||
declared_type = multi_decl['declared_type'] | ||
(end_line_no, multi_decl_str) = get_multi_decls_as_str(multi_decl, ORIGIN_LINES) | ||
|
||
decl_list = get_decl_list(multi_decl_str, declared_type) | ||
return (start_line_no, end_line_no, declared_type, decl_list) | ||
|
||
def get_multi_decls_as_str(multi_decl, ORIGIN_LINES): | ||
"""This function will remove comments in multiple declarations statement.""" | ||
decl_str_list = list() | ||
cur_line_no = multi_decl['line'] | ||
is_end_line = False | ||
has_unclosed_comment = False | ||
|
||
while not is_end_line: | ||
cur_line = ORIGIN_LINES[cur_line_no - 1] | ||
(processed_line, is_end_line, has_unclosed_comment) = process_line(cur_line, has_unclosed_comment) | ||
decl_str_list.append(processed_line) | ||
cur_line_no += 1 | ||
|
||
reformat_multi_decls_str = " ".join("".join(decl_str_list).split()); | ||
return (cur_line_no - 1, reformat_multi_decls_str) | ||
|
||
def process_line(line, has_unclosed_comment): | ||
if has_unclosed_comment: | ||
i = 0 | ||
while i < len(line): | ||
if line[i] == '*' and i < len(line) - 1: | ||
i += 1 | ||
if line[i] == '/': | ||
return process_line(line[i + 1:], False) | ||
i += 1 | ||
return ("", False, True) # empty line, not end line, has unclosed comment | ||
|
||
i = 0 | ||
while i < len(line): | ||
# if meet a '/' then is must be '//' or '/*' for a well-grammered line | ||
if line[i] == '/' and i < len(line) - 1: | ||
cmt_start_idx = i; | ||
i += 1 | ||
if line[i] == '/': | ||
## this line is a single line comment | ||
return (line[:i - 1], False, False) #(remaining line, not end line, no unclosed comment) | ||
elif line[i] == '*': | ||
i += 1 | ||
while i < len(line): | ||
if line[i] == '*' and i < len(line) - 1: | ||
i += 1 | ||
if line[i] == '/': | ||
# recursive produce line: (line before cmt + line after cmt) | ||
return process_line(line[:cmt_start_idx] + line[i + 1:], False) | ||
i += 1 | ||
return (line[:cmt_start_idx], False, True) | ||
|
||
if line[i] == ';': | ||
return (line[:i], True, False) | ||
i += 1 | ||
|
||
return (line, False, False) | ||
|
||
def get_decl_list(decls_str, declared_type): | ||
""" public int[] a = {1,2,3}, b = new int[0], c = Test.<Object, Object>m(a, b), d = new int [2], e = {1,2,3} | ||
""" | ||
#remove type first | ||
decls_str = decls_str.replace(declared_type, '').strip() | ||
decls_list = list() | ||
LEFT_SET = set(['(', '<', '{']) | ||
RIGHT_SET = set([')', '>', '}']) | ||
# indicate whether current iterating in a parameter decalration like "<x, x>" or "{x, x}" or "(x, x)" | ||
in_param_level = 0 | ||
|
||
i = 0 | ||
start_idx = i | ||
while i < len(decls_str): | ||
if decls_str[i] in LEFT_SET: | ||
in_param_level += 1 | ||
elif decls_str[i] in RIGHT_SET: | ||
in_param_level -= 1 | ||
assert in_param_level >= 0 | ||
elif decls_str[i] == ',' and in_param_level == 0: | ||
decls_list.append(str(decls_str[start_idx:i]).strip()) | ||
start_idx = i + 1 | ||
i += 1 | ||
|
||
if start_idx < len(decls_str): | ||
decls_list.append(str(decls_str[start_idx:]).strip()) | ||
|
||
return decls_list | ||
|
||
|
||
def collect_by_file(multi_decls): | ||
file_based_multi_decls = dict() | ||
|
||
for decl in multi_decls: | ||
file_path = decl['file'] | ||
if not file_path in file_based_multi_decls: | ||
file_based_multi_decls[file_path] = list() | ||
decl.pop('file') | ||
file_based_multi_decls[file_path].append(decl) | ||
return file_based_multi_decls | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/bin/bash | ||
SCRIPT_DIR=$(cd $(dirname "$0") && pwd) | ||
ROOT_DIR=$(cd $SCRIPT_DIR/.. && pwd) | ||
java -cp $ROOT_DIR/checkstyle/target/classes:$(ls $ROOT_DIR/checkstyle/target/dependency/*.jar | tr '\n' : | rev | cut -c 2- | rev) com.puppycrawl.tools.checkstyle.Main -c multiDeclChecks.xml -f json "$@" | ||
# java -cp $ROOT_DIR/checkstyle/target/classes:$(ls $ROOT_DIR/checkstyle/target/dependency/*.jar | tr '\n' : | rev | cut -c 2- | rev) com.puppycrawl.tools.checkstyle.Main -t "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/bin/bash | ||
SCRIPT_DIR=$(cd $(dirname "$0") && pwd) | ||
ROOT_DIR=$(cd $SCRIPT_DIR/.. && pwd) | ||
java -cp $ROOT_DIR/checkstyle/target/classes:$(ls $ROOT_DIR/checkstyle/target/dependency/*.jar | tr '\n' : | rev | cut -c 2- | rev) com.puppycrawl.tools.checkstyle.Main -c $SCRIPT_DIR/multiDeclChecks.xml -f json "$@" | python $SCRIPT_DIR/multiDeclRefactor.py | ||
# java -cp $ROOT_DIR/checkstyle/target/classes:$(ls $ROOT_DIR/checkstyle/target/dependency/*.jar | tr '\n' : | rev | cut -c 2- | rev) com.puppycrawl.tools.checkstyle.Main -t "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import java.util.*; | ||
public class Test { | ||
protected int f; | ||
protected int g; | ||
|
||
public int[] a = {1,2,3}; | ||
public int[] b = new int[0]; | ||
public int[] c = Test.<Object, Object>m(a, b); | ||
public int[] d = new int [2]; | ||
public int[] e = {1,2,3}; | ||
|
||
public int i; | ||
public int j; | ||
|
||
static <T1, T2> int[] m(T1 t1, T2 t2) { | ||
return new int[0]; | ||
} | ||
} | ||
|
||
class MyType {} |