-
Notifications
You must be signed in to change notification settings - Fork 0
Core functionality to calculate metrics of single method #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
build.gradle
Outdated
| apply plugin: 'application' | ||
| apply plugin: 'pmd' | ||
|
|
||
| group 'com.github' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to start with correct group - for example - you nick
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean com.github.aravij?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
build.gradle
Outdated
| apply plugin: 'pmd' | ||
|
|
||
| group 'com.github' | ||
| version '1.0-SNAPSHOT' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to use proper versioning. I strictly suggest https://semver.org/
build.gradle
Outdated
| } | ||
|
|
||
| dependencies { | ||
| implementation group: 'net.sourceforge.pmd', name: 'pmd-java', version: '6.29.0' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is fine to skip explicit "group"/"name"
example:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean to change it to
implementation "net.sourceforge.pmd:pmd-java:6.29.0"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep
|
|
||
| application { | ||
| mainClassName = 'com.github.emcat.Emcat' | ||
| applicationDefaultJvmArgs = ["--enable-preview"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better not to use preview features
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In further PRs I get rid of preview mode and migrate to java 11.
We can keep it here and further PRs will fix this problem.
|
use proper branch naming, like feature/my_feature |
In my word naming with number is a proper way. It allows to omit collision in names. And you can easily find it among issues, to read full text. |
this is not a best practice |
|
Also better to use MIT license |
|
|
||
| int exitCode; | ||
| if (methodMetrics == null) { | ||
| System.err.println("Failed to calculate metrics"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Java has a good logging mechanism, please use log4j, slf4j
| private transient String className; | ||
|
|
||
| @Option(names = {"-m", "--method_name"}, required = true, description = "Method name to analyse") | ||
| private transient String methodName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why transient here? no need for it, not related to serializaion
| @Command(name = "emcat", description = "Calculates given NCSS and cyclomatic complexity of a given method.", version = "1.0") | ||
| public class Emcat implements Callable<Integer> { | ||
| @Option(names = {"-f", "--file"}, required = true, description = "File path to Java source code to analyse") | ||
| private transient String sourceCodeFilePath; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same about transient
| } | ||
|
|
||
| public static void main(final String... args) { | ||
| System.exit(new CommandLine(new Emcat()).execute(args));//NOPMD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why System.exit here? Better to exit in the place where you really want to exit.
System.exit should be used when the situation is extraordinary. Main should just simply return. It always return 0 by the default.
| import com.github.emcat.metric_calculators.MethodMetricsData; | ||
|
|
||
| @Command(name = "emcat", description = "Calculates given NCSS and cyclomatic complexity of a given method.", version = "1.0") | ||
| public class Emcat implements Callable<Integer> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need callable interface here? No need for it, in your case you can just use a class.
| if (methodMetrics == null) { | ||
| System.err.println("Failed to calculate metrics"); | ||
| System.err.println(methodMetricsAggregator.getException()); | ||
| exitCode = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can exit here
|
|
||
| return metricsCalculator.calculateMethodMetrics(methodDeclaration); | ||
| } | ||
| catch (Exception exception) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very bad practice to catch all exceptions here.
https://stackoverflow.com/questions/2416316/why-is-the-catchexception-almost-always-a-bad-idea
| } | ||
| } | ||
|
|
||
| private Parser javaParser = new JavaLanguageModule().getDefaultVersion().getLanguageVersionHandler().getParser(new ParserOptions()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to put properties in the beginning of the class
|
|
||
| private ASTClassOrInterfaceDeclaration getClassDeclaration(String filename, String className) throws IOException { | ||
| ASTCompilationUnit compilationUnit = getCompilationUnit(filename); | ||
| for (JavaNode child : compilationUnit.children()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to use great Java features - use lambda forEach( child -> )
| } | ||
| } | ||
|
|
||
| throw new RuntimeException(String.format("There is no class named %s in file %s", className, filename)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better not to build interaction logic on exceptions. Exit here with an error or return something good.
|
|
||
| private ASTMethodDeclaration getMethodDeclaration(String filename, String className, String methodName) throws IOException { | ||
| ASTClassOrInterfaceDeclaration classDeclaration = getClassDeclaration(filename, className); | ||
| for (ASTAnyTypeBodyDeclaration classInnerDeclaration : classDeclaration.getDeclarations()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to use great Java features - use lambda forEach( classInnerDeclaration -> )
|
|
||
| private Parser javaParser = new JavaLanguageModule().getDefaultVersion().getLanguageVersionHandler().getParser(new ParserOptions()); | ||
| private MethodMetricsCalculator metricsCalculator = new MethodMetricsCalculator(); | ||
| private MethodProcessingException exception = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to initialize fields with null explicitly, in classes they are already initialized
| } | ||
| } | ||
|
|
||
| throw new RuntimeException(String.format("There is no method named %s in class named %s in file %s", methodName, className, filename)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same about exceptions as below, bad idea to build your logic using it. But if you really want to build your logic using that - at least create your own runtime exception, or choose suitable from existing
|
|
||
| public MethodMetricsData calculateMethodMetrics(final ASTMethodOrConstructorDeclaration declarationNode) { | ||
| return new MethodMetricsData( | ||
| (int) ncssCalculator.computeFor(declarationNode, MetricOptions.emptyOptions()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to be extremely careful here. Double is a bigger type so here it can cause a problem
May be it will be fine to check number ranges first
| import net.sourceforge.pmd.lang.metrics.MetricOptions; | ||
|
|
||
| public class MethodMetricsCalculator { | ||
| private final transient CycloMetric cyclomaticComplexityCalculator = new CycloMetric(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why transient here? Not needed
| private final transient CycloMetric cyclomaticComplexityCalculator = new CycloMetric(); | ||
| private final transient NcssMetric.NcssOperationMetric ncssCalculator = new NcssMetric.NcssOperationMetric(); | ||
|
|
||
| public MethodMetricsData calculateMethodMetrics(final ASTMethodOrConstructorDeclaration declarationNode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final here is useless
| private final transient CycloMetric cyclomaticComplexityCalculator = new CycloMetric(); | ||
| private final transient NcssMetric.NcssOperationMetric ncssCalculator = new NcssMetric.NcssOperationMetric(); | ||
|
|
||
| public MethodMetricsData calculateMethodMetrics(final ASTMethodOrConstructorDeclaration declarationNode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Java you can create constructor with arguments.
I suggest you to create a constructor here MethodMetricsCalculator (ASTMethodOrConstructorDeclaration declarationNode) with argument and do all the logic in it (for example to fill some MethodMetricsData field )
|
|
||
| private static final long serialVersionUID = -2272341134842435255L; | ||
|
|
||
| public MethodProcessingException(final MethodDescriptor method, final Exception exception) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, wrapping an exception is extremely bad practice
orchestr7
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to read my comments, especially with logic related to exceptions
| package com.github.emcat.metric_calculators; | ||
|
|
||
| import net.sourceforge.pmd.lang.Parser; | ||
| import net.sourceforge.pmd.lang.ParserOptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all the code in this class is very heavy. I suggest you to create intermediate variables to make it less complex, for example instead of:
for (String a: foo.bar.test() ) {}
do:
String all = foo.bar.test();
all.forEach(a -> {} );
Adds test, picocli for cmd arguments parsing, PMD for checking format.
Two large files: gradlew and gradle.bat are autogenerated and need no review.