Automatic code simplification via refactoring [closed]
Asked Answered
S

2

0

Is there a tool for automatic java code simplification via refactoring?

There are such tools for Python and C:

but I'm not aware of any such program for Java.

There are many cases where a tool could automatically simplify code, like e.g.:

  • loops: for (String s: a) { b.add(s); }b.addAll(a);
  • if/return: if (x) return true; else return false;return x;
  • ternary operator: if (x) {result = a;} else {result = b;}result = x ? a : b;
  • automatic refactor to diamond operator / exception multi catch / lambdas
  • lots more...

The advantage of such automatic refactor is removal of lots of boilerplate lines of code, making it more clean and conceise.

Swinton answered 23/1, 2015 at 22:27 Comment(5)
The question is off-topic: Questions asking us to recommend or find a book, tool, software library, tutorial or other off-site resource are off-topic for Stack OverflowPeltry
if (x) return true; → return x; ???Programme
@Ira good point, it's en error :) editedSwinton
If you want other concrete suggestions, you should take your request to Stack Exchange's Software Recommendations sister site. –Programme
Other than Ira Baxter's answer, I haven't heard about anything. Your IDE can do stuff, but usually not automatically. There are static analysis tools that can help show yout he pieces of code that need changing. Maybe there are some plugins for the IDEs? Otherwise, you'll need some kind of parser + a lot of rules. I heard Refaster will bee good for this kind of stuff when it's finally released. Also, autorefactor.org looks like a hit.Crouton
P
1

Sure. Check out Program Transformation Tools (PTS). Such tools provide a means to define a grammar, use that grammar to parse source text to ASTs, and prettyprint ASTs back to text. More importantly, the same grammar is used to read source-to-source transformation rules, that allow you to express transformations directly (or express compound transformations in terms of sets of rewrites). Such transformations use the expressed patterns to match against the ASTs, and modify the ASTs; these transformations, being structure based, are not confused by layout or whitespace.

As an example, our DMS Software Reengineering Toolkit let you write rules in the form if you see this, replace it by that, taking the general form:

 rule rule_name(pattern_variables)
   : pattern_syntax_category -> replacement_syntax_category
 = metaquote  pattern_text_in_specified_language metaquote
 => metaquote replacement_text_in_specified_language metaquote
 if condition_over_bound_pattern_variables;

where

  • rule is syntax introducing a rule,
  • rule_name gives the rule a name to distinguish it from other rules,
  • pattern_variables is a list of pairs n:c of a pattern variable name n and syntax category c which n must satisfy,
  • pattern_syntax_category is a syntax category that the pattern_text must satisfy,
  • replacement_syntax_category is a syntax category that the replacement_text must satisfy. Usually, and in all examples given here, the same as pattern_syntax_category ; when using rewrite rules to translate between languages, these often differ because the syntax categories in the languages differ.
  • metaquote is the " character, which seperates the rule language syntax from that of the pattern or replacement language syntax
  • pattern_text_in_specified_language is a well-formed fragment of the source language, containing pattern variables n written as \n; any match of pattern binds the pattern variables to subtrees. If \n occurs twice in pattern, both instances must be identical.
  • replacement_text_in_specified_language is a well-formed fragment of the target langauge. A found pattern is replaced by the replacement, with any pattern variables in the replacement substitued by the bound pattern variable value
  • if is syntax introducing a condition
  • condition_over_bound_pattern_variables is a constraint that the pattern variables must satisfy after a pattern match has been found. This is often used to check context conditions that must be satisified.

DMS will let you write and apply the following example transformations (including some of OP's examples):

 default domain Java~v8; -- specify v8 dialect of Java to manipulate

 rule reduce_strength_squared(e: term):
     :product -> product
 = "\e ^ 2 " ==> "\e * \e "
 if no_side_effects(e);

 rule optimize_divide_by_self(e: term):
     :product -> product
 = " \e / \e " => " 1 " if is_not_zero(e);

 rule accumulate_string(n: IDENTIFIER, a: expression, b: left_hand_side)
   : statement -> statement
 = "for (String \s: \a) { \b.add(\s); }"
 => "\b.addAll(\a);";

 rule eliminate_useless_if(x: expression, s: statement)
    : statement -> statement
 = "if (\x) \s; else \s; " -- generalizes OP's example
 => "\s;";

 rule left_factor_ternary(x: expression; t: left_hand_side; a: expression; b:expression)
      : statement -> statement
 =  "if (\x) { \t = \a;} else {\t = \b;} "
 =>  "\t = \x ? \a : \b ";

 rule convert_to_diamond( T1: qualified_path, T2: qualified_path,
                           C: qualified_path, 
                           i: IDENTIFIER, a: arglist)
       :statement -> statement
 =  "\T1<\T2> \i = new \C<\T2>(\a);"
 => "\T1<\T2> \i = new \C<>(\a);"

 rule merge_multi_catch( b:body, i1: IDENTIFIER, e1: qualified_path,
                                  i2: IDENTIFIER,  eh: body  )
       :statement -> statement
  = "try { \b }
     catch ( \i1: \e1 ) { \eh }
     catch ( \i2: \e1 ) { \eh }";
  => "try { \b }
     catch ( \i1, \i2: \e1 ) { \eh }";

More sophisticated transformations are possible, including those that affect parts of the program that are far apart and even across source file boundaries. These usually require some additional metaprogramming (not discussed here) and often some additional checks on the context, e.g., that identifiers are of the right type, etc. [DMS has full symbol tables for Java to support this].

Other PTS can express rules in similar way. Most of them don't provide support for deeper semantic facts such as symbol tables or types, although the claim is that you can program those yourself. Experience shows this is a lot of work, and you really want it to be right. (Who wants their transformation to damage the code because it is operating on bad information?).

Programme answered 29/1, 2015 at 1:27 Comment(0)
T
2

I am the author of AutoRefactor, mentioned by Slanec. All the first refactorings you showed are already implemented, but the following are not yet:

  • automatic refactor to diamond operator / exception multi catch / lambdas
  • lots more...

See the open issues, including Java 7 refactorings. For the moment I have only implemented Java 6 compatible refactorings.

Thyself answered 16/2, 2015 at 22:18 Comment(1)
The following are now implemented: "automatic refactor to diamond operator / exception multi catch"Thyself
P
1

Sure. Check out Program Transformation Tools (PTS). Such tools provide a means to define a grammar, use that grammar to parse source text to ASTs, and prettyprint ASTs back to text. More importantly, the same grammar is used to read source-to-source transformation rules, that allow you to express transformations directly (or express compound transformations in terms of sets of rewrites). Such transformations use the expressed patterns to match against the ASTs, and modify the ASTs; these transformations, being structure based, are not confused by layout or whitespace.

As an example, our DMS Software Reengineering Toolkit let you write rules in the form if you see this, replace it by that, taking the general form:

 rule rule_name(pattern_variables)
   : pattern_syntax_category -> replacement_syntax_category
 = metaquote  pattern_text_in_specified_language metaquote
 => metaquote replacement_text_in_specified_language metaquote
 if condition_over_bound_pattern_variables;

where

  • rule is syntax introducing a rule,
  • rule_name gives the rule a name to distinguish it from other rules,
  • pattern_variables is a list of pairs n:c of a pattern variable name n and syntax category c which n must satisfy,
  • pattern_syntax_category is a syntax category that the pattern_text must satisfy,
  • replacement_syntax_category is a syntax category that the replacement_text must satisfy. Usually, and in all examples given here, the same as pattern_syntax_category ; when using rewrite rules to translate between languages, these often differ because the syntax categories in the languages differ.
  • metaquote is the " character, which seperates the rule language syntax from that of the pattern or replacement language syntax
  • pattern_text_in_specified_language is a well-formed fragment of the source language, containing pattern variables n written as \n; any match of pattern binds the pattern variables to subtrees. If \n occurs twice in pattern, both instances must be identical.
  • replacement_text_in_specified_language is a well-formed fragment of the target langauge. A found pattern is replaced by the replacement, with any pattern variables in the replacement substitued by the bound pattern variable value
  • if is syntax introducing a condition
  • condition_over_bound_pattern_variables is a constraint that the pattern variables must satisfy after a pattern match has been found. This is often used to check context conditions that must be satisified.

DMS will let you write and apply the following example transformations (including some of OP's examples):

 default domain Java~v8; -- specify v8 dialect of Java to manipulate

 rule reduce_strength_squared(e: term):
     :product -> product
 = "\e ^ 2 " ==> "\e * \e "
 if no_side_effects(e);

 rule optimize_divide_by_self(e: term):
     :product -> product
 = " \e / \e " => " 1 " if is_not_zero(e);

 rule accumulate_string(n: IDENTIFIER, a: expression, b: left_hand_side)
   : statement -> statement
 = "for (String \s: \a) { \b.add(\s); }"
 => "\b.addAll(\a);";

 rule eliminate_useless_if(x: expression, s: statement)
    : statement -> statement
 = "if (\x) \s; else \s; " -- generalizes OP's example
 => "\s;";

 rule left_factor_ternary(x: expression; t: left_hand_side; a: expression; b:expression)
      : statement -> statement
 =  "if (\x) { \t = \a;} else {\t = \b;} "
 =>  "\t = \x ? \a : \b ";

 rule convert_to_diamond( T1: qualified_path, T2: qualified_path,
                           C: qualified_path, 
                           i: IDENTIFIER, a: arglist)
       :statement -> statement
 =  "\T1<\T2> \i = new \C<\T2>(\a);"
 => "\T1<\T2> \i = new \C<>(\a);"

 rule merge_multi_catch( b:body, i1: IDENTIFIER, e1: qualified_path,
                                  i2: IDENTIFIER,  eh: body  )
       :statement -> statement
  = "try { \b }
     catch ( \i1: \e1 ) { \eh }
     catch ( \i2: \e1 ) { \eh }";
  => "try { \b }
     catch ( \i1, \i2: \e1 ) { \eh }";

More sophisticated transformations are possible, including those that affect parts of the program that are far apart and even across source file boundaries. These usually require some additional metaprogramming (not discussed here) and often some additional checks on the context, e.g., that identifiers are of the right type, etc. [DMS has full symbol tables for Java to support this].

Other PTS can express rules in similar way. Most of them don't provide support for deeper semantic facts such as symbol tables or types, although the claim is that you can program those yourself. Experience shows this is a lot of work, and you really want it to be right. (Who wants their transformation to damage the code because it is operating on bad information?).

Programme answered 29/1, 2015 at 1:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.