Skip to content

Conversation

@aravij
Copy link
Owner

@aravij aravij commented Dec 22, 2020

Adds test, picocli for cmd arguments parsing, PMD for checking format.

Two large files: gradlew and gradle.bat are autogenerated and need no review.

@aravij aravij self-assigned this Dec 22, 2020
@aravij aravij linked an issue Dec 22, 2020 that may be closed by this pull request
@aravij aravij added the enhancement New feature or request label Dec 22, 2020
build.gradle Outdated
apply plugin: 'application'
apply plugin: 'pmd'

group 'com.github'

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

Copy link
Owner Author

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?

Copy link
Owner Author

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'

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'
Copy link

@orchestr7 orchestr7 Dec 24, 2020

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"

Copy link
Owner Author

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"

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"]

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

Copy link
Owner Author

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.

@orchestr7
Copy link

use proper branch naming, like feature/my_feature

@aravij
Copy link
Owner Author

aravij commented Dec 24, 2020

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.

@orchestr7
Copy link

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

@orchestr7
Copy link

Also better to use MIT license


int exitCode;
if (methodMetrics == null) {
System.err.println("Failed to calculate metrics");

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;

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;

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
Copy link

@orchestr7 orchestr7 Dec 24, 2020

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> {

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;

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) {
Copy link

@orchestr7 orchestr7 Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

private Parser javaParser = new JavaLanguageModule().getDefaultVersion().getLanguageVersionHandler().getParser(new ParserOptions());

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()) {

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));

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()) {
Copy link

@orchestr7 orchestr7 Dec 24, 2020

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;

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));

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()),
Copy link

@orchestr7 orchestr7 Dec 24, 2020

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();

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) {

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) {

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) {

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

Copy link

@orchestr7 orchestr7 left a 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;
Copy link

@orchestr7 orchestr7 Dec 24, 2020

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 -> {} );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Basic functionality

3 participants