8

Is there a tool that searches code and makes recommendations for extracting method candidates with duplicated code? Here's a really contrived example:

import java.util.List;

public class ExtractMethod {
  public boolean mode;

  public String getFirst(List<String> one, List<String> two) {
    if(mode) {
      StringBuilder sb = new StringBuilder();
      for(String elements : one) {
        sb.append(elements.charAt(0));
      }
      return sb.toString();
    } else {
      StringBuilder sb = new StringBuilder();
      for(String elements : two) {
        sb.append(elements.charAt(0));
      }
      return sb.toString();
    }
  }
}

If you select the duplicated lines in Eclipse, and select Extract Method, you get this:

import java.util.List;

public class ExtractMethod {
  public boolean mode;

  public String getFirst(List<String> one, List<String> two) {
    if(mode) {
      return getFirstFromList(one);
    } else {
      return getFirstFromList(two);
    }
  }

  private String getFirstFromList(List<String> one) {
    StringBuilder sb = new StringBuilder();
    for(String elements : one) {
      sb.append(elements.charAt(0));
    }
    return sb.toString();
  }
}

Pretty great. Is there a tool that looks through a class and makes recommendations about good candidates for doing this?

3

Popular free open-source PMD code analyzer has a copy-paste detector CPD - it can find similar code fragments which are good candidates to "extract method" refactoring. It is cross-platform and can be run in any system that have JRE implemented. It also have a vast variety of plugins for popular build tools and IDEs like maven, eclipse, ant, etc.

  • Added a bit more information. – Konstantin V. Salikhov May 1 '14 at 16:00
  • +1 This seems great! I'll accept after trying it out. – durron597 May 1 '14 at 16:08
  • Looks great now :) – Nick Wilde May 1 '14 at 16:17
  • Okay actually this is not what I'm looking for, as far as I can tell this just looks for exact matches, not "similar code" – durron597 May 1 '14 at 16:30
  • Also it requires that the tile size be at least 25. It doesn't find any duplication in the example provided in the OP. – durron597 May 1 '14 at 16:31
1

Our CloneDR tool will do this, detecting clones across thousands of files. You don't give it anything but the files; it determines the clones automatically.

A clone set is a set of code blocks that are deemed similar. Often clone sets are just pairs, sometimes of thousands of lines, this happens when source files are copied en masse in the code base. Remarkably, clone sets may have 3, 100, up to several hundred instances of similar code; usually the individual clone in the latter case is 4-10 lines and represents some idiom used everywhere in the code base (e.g., a string-to-number conversion routine).

Among the almost 10,000 clone sets it finds in the over 1MSLOC of code comprising a version of the Eclipse JDT, it will find these two clones:

    this.declaringQualification = isCaseSensitive() ? declaringQualification:  CharOperation.toLowerCase(declaringQualification);
    this.declaringSimpleName = isCaseSensitive() ? declaringSimpleName:  CharOperation.toLowerCase(declaringSimpleName);
    this.typeQualification = isCaseSensitive() ? typeQualification:  CharOperation.toLowerCase(typeQualification);

and

    this.declaringQualification = isCaseSensitive() ? declaringQualification:  CharOperation.toLowerCase(declaringQualification);
    this.declaringSimpleName = isCaseSensitive() ? declaringSimpleName:  CharOperation.toLowerCase(declaringSimpleName);
    this.returnQualification = isCaseSensitive() ? returnQualification:  CharOperation.toLowerCase(returnQualification);

and suggest this parameterized abstraction (not quite a method, but pretty close:

this.declaringQualification = isCaseSensitive() ? declaringQualification: CharOperation.toLowerCase(declaringQualification);
this.declaringSimpleName = isCaseSensitive() ? declaringSimpleName: CharOperation.toLowerCase(declaringSimpleName);
this. [[#variable5a6b82a0]]= isCaseSensitive() ? [[#variable5a6b82a0]]: CharOperation.toLowerCase( [[#variable5a6b82a0]]);

Notice that it decided the code has only one parameter, not two.

It will find bigger expressions, even statements, as parameters; in the latter case, it effectively suggests that you need a lambda as a parameter.

You can control the size of a clone, how many parameters are allowed, and how similar the code chunks have to be to be considered clones. On OP's contrived example in one file, with the right settings, it should detect the inner blocks as clones with a single parameter. (Nobody runs examples that small in practice.)

(The site has many examples from many languages).

  • This is pretty cool, I'll have to take a look at it later. By the way, please don't create new tags that don't add to the question. See the help center – durron597 Jun 2 '15 at 3:10
  • "copy-paste-detection" is a widely known term for clone-detection tools. (I always thought "clone detection" was a fine term, but the world seems to think otherwise). – Ira Baxter Jun 2 '15 at 3:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.