diff --git a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java index f4ef343..54a69b3 100644 --- a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java +++ b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java @@ -20,21 +20,7 @@ import java.util.Collections; import java.util.List; -import fr.greencodeinitiative.java.checks.ArrayCopyCheck; -import fr.greencodeinitiative.java.checks.AvoidFullSQLRequest; -import fr.greencodeinitiative.java.checks.AvoidGettingSizeCollectionInLoop; -import fr.greencodeinitiative.java.checks.AvoidMultipleIfElseStatement; -import fr.greencodeinitiative.java.checks.AvoidRegexPatternNotStatic; -import fr.greencodeinitiative.java.checks.AvoidSQLRequestInLoop; -import fr.greencodeinitiative.java.checks.AvoidSetConstantInBatchUpdate; -import fr.greencodeinitiative.java.checks.AvoidSpringRepositoryCallInLoopOrStreamCheck; -import fr.greencodeinitiative.java.checks.AvoidStatementForDMLQueries; -import fr.greencodeinitiative.java.checks.AvoidUsageOfStaticCollections; -import fr.greencodeinitiative.java.checks.FreeResourcesOfAutoCloseableInterface; -import fr.greencodeinitiative.java.checks.IncrementCheck; -import fr.greencodeinitiative.java.checks.InitializeBufferWithAppropriateSize; -import fr.greencodeinitiative.java.checks.NoFunctionCallWhenDeclaringForLoop; -import fr.greencodeinitiative.java.checks.OptimizeReadFileExceptions; +import fr.greencodeinitiative.java.checks.*; import org.sonar.plugins.java.api.CheckRegistrar; import org.sonar.plugins.java.api.JavaCheck; import org.sonarsource.api.sonarlint.SonarLintSide; @@ -62,7 +48,8 @@ public class JavaCheckRegistrar implements CheckRegistrar { InitializeBufferWithAppropriateSize.class, AvoidSetConstantInBatchUpdate.class, FreeResourcesOfAutoCloseableInterface.class, - AvoidMultipleIfElseStatement.class + AvoidMultipleIfElseStatement.class, + UseFetchTypeLazyRule.class ); /** diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseFetchTypeLazyRule.java b/src/main/java/fr/greencodeinitiative/java/checks/UseFetchTypeLazyRule.java new file mode 100644 index 0000000..122cb3d --- /dev/null +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseFetchTypeLazyRule.java @@ -0,0 +1,74 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.tree.*; + +@Rule(key = "EC205") +public class UseFetchTypeLazyRule extends BaseTreeVisitor implements JavaFileScanner { + protected static final String ONE_TO_MANY = "OneToMany"; + protected static final String MESSAGE_RULE = "Avoid Using FetchType.EAGER instead of FetchType.LAZY on collections in JPA Entity"; + protected static final String JPA_ANNOTATION_WITHOUT_FETCH_TYPE_DETECTED = "JPA annotation without FetchType detected"; + protected static final String LAZY = "LAZY"; + protected static final String MANY_TO_MANY = "ManyToMany"; + private JavaFileScannerContext context; + + @Override + public void scanFile(JavaFileScannerContext javaFileScannerContext) { + this.context = javaFileScannerContext; + // The call to the scan method on the root of the tree triggers the visit of the AST by this visitor + scan(context.getTree()); + } + + @Override + public void visitAnnotation(AnnotationTree annotationTree) { + String annotationName = ((IdentifierTree) annotationTree.annotationType()).name(); + if (ONE_TO_MANY.equals(annotationName) + || MANY_TO_MANY.equals(annotationName)) { + boolean fetchExist = false; + ExpressionTree fetchTypeArg = null; + + for (ExpressionTree argument : annotationTree.arguments()) { + if (argument.is(Tree.Kind.ASSIGNMENT)) { + AssignmentExpressionTree assignmentInvocation = (AssignmentExpressionTree) argument; + if (assignmentInvocation.variable().toString().equals("fetch")) { + fetchExist = true; + fetchTypeArg = assignmentInvocation.expression(); + } + } + } + + this.reportFetchTypeIssue(fetchExist,fetchTypeArg,annotationTree); + } + super.visitAnnotation(annotationTree); + } + + private void reportFetchTypeIssue(boolean fetchExist, ExpressionTree fetchTypeArg, AnnotationTree annotationTree){ + if (!fetchExist) { + context.reportIssue(this, annotationTree, JPA_ANNOTATION_WITHOUT_FETCH_TYPE_DETECTED); + } else if (fetchTypeArg != null) { + String fetchType = ((MemberSelectExpressionTree) fetchTypeArg).identifier().name(); + if (!LAZY.equals(fetchType.strip())) { + context.reportIssue(this, annotationTree, MESSAGE_RULE); + } + } + } +} diff --git a/src/test/files/UseFetchTypeLazyRule.java b/src/test/files/UseFetchTypeLazyRule.java new file mode 100644 index 0000000..65db55c --- /dev/null +++ b/src/test/files/UseFetchTypeLazyRule.java @@ -0,0 +1,50 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import javax.persistence.OneToMany; +import javax.persistence.ManyToOne; +import java.util.Collections; + +public class UseFetchTypeLazyRuleTest { + + @OneToMany(mappedBy = "firstEntity") // Noncompliant + private Collection firstEntities; + + @ManyToMany(mappedBy = "firstEntity1") // Noncompliant + private Collection firstEntities1; + + @OneToMany // Noncompliant + private Collection secondEntities1; + + @ManyToMany // Noncompliant + private Collection secondEntities2; + + @OneToMany(mappedBy = "thirdEntity1", fetch= FetchType.EAGER) // Noncompliant + private Collection thirdEntities1; + + @ManyToMany(mappedBy = "thirdEntity1", fetch= FetchType.EAGER) // Noncompliant + private Collection thirdEntities2; + + @OneToMany(fetch = FetchType.LAZY) // Compliant + private Collection fourthEntities1; + + @ManyToMany(fetch = FetchType.LAZY) // Compliant + private Collection fourthEntities2; + +} diff --git a/src/test/java/fr/greencodeinitiative/java/JavaCheckRegistrarTest.java b/src/test/java/fr/greencodeinitiative/java/JavaCheckRegistrarTest.java index 02270ca..552d683 100644 --- a/src/test/java/fr/greencodeinitiative/java/JavaCheckRegistrarTest.java +++ b/src/test/java/fr/greencodeinitiative/java/JavaCheckRegistrarTest.java @@ -31,9 +31,8 @@ void checkNumberRules() { final JavaCheckRegistrar registrar = new JavaCheckRegistrar(); registrar.register(context); - assertThat(context.checkClasses()).hasSize(15); + assertThat(context.checkClasses()).hasSize(16); assertThat(context.testCheckClasses()).isEmpty(); } - } diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UseFetchTypeLazyRuleTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseFetchTypeLazyRuleTest.java new file mode 100644 index 0000000..97e16f0 --- /dev/null +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseFetchTypeLazyRuleTest.java @@ -0,0 +1,32 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +class UseFetchTypeLazyRuleTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseFetchTypeLazyRule.java") + .withCheck(new UseFetchTypeLazyRule()) + .verifyIssues(); + } +}