2025 init
This commit is contained in:
20
README.md
Normal file
20
README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Validation and Verification: Practical Session #2
|
||||||
|
|
||||||
|
The goal of this practical session is to use and implement static analysis tools to compute metrics such as Cyclomatic Complexity(CC) or Tight Class Cohesion (TCC).
|
||||||
|
After this session you should be able to use existing static analysis tools, and to extend them to implement your own analysis.
|
||||||
|
|
||||||
|
## Exercises
|
||||||
|
|
||||||
|
You can access the exercises [here](sujet.md)
|
||||||
|
|
||||||
|
## Lab implementation
|
||||||
|
|
||||||
|
You can realize this lab by group of 1 or 2.
|
||||||
|
|
||||||
|
## Deliverable and evaluation
|
||||||
|
|
||||||
|
This lab will be graded. You must **fork this repository** and submit a **merge request** with your answers directly here.
|
||||||
|
|
||||||
|
**Deadline:** December 19th, 2025, at 23:59.
|
||||||
|
|
||||||
|
**Important:** To be considered for grading, all members of the group must be **tagged in the description** of the merge request.
|
||||||
3
code/Exercise3/README.md
Normal file
3
code/Exercise3/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Code of your exercise
|
||||||
|
|
||||||
|
Put here all the code created for this exercise
|
||||||
3
code/Exercise4/README.md
Normal file
3
code/Exercise4/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Code of your exercise
|
||||||
|
|
||||||
|
Put here all the code created for this exercise
|
||||||
3
code/Exercise5/README.md
Normal file
3
code/Exercise5/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Code of your exercise
|
||||||
|
|
||||||
|
Put here all the code created for this exercise
|
||||||
3
code/Exercise6/README.md
Normal file
3
code/Exercise6/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Code of your exercise
|
||||||
|
|
||||||
|
Put here all the code created for this exercise
|
||||||
69
code/javaparser-starter/pom.xml
Normal file
69
code/javaparser-starter/pom.xml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>fr.istic.vv</groupId>
|
||||||
|
<artifactId>javaparser-starter</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>9</maven.compiler.source>
|
||||||
|
<maven.compiler.target>9</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.javaparser</groupId>
|
||||||
|
<artifactId>javaparser-core</artifactId>
|
||||||
|
<version>3.16.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<!-- Creates a jar and sets the main class so it can be executed directly wit java -jar .-->
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.1.1</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<mainClass>fr.istic.vv.Main</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Creates a jar containing the compiled code and all dependencies. Useful for distribution. -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<mainClass>fr.istic.vv.Main</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-my-jar-with-dependencies</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
39
code/javaparser-starter/src/main/java/fr/istic/vv/Main.java
Normal file
39
code/javaparser-starter/src/main/java/fr/istic/vv/Main.java
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package fr.istic.vv;
|
||||||
|
|
||||||
|
import com.github.javaparser.Problem;
|
||||||
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
|
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
|
||||||
|
import com.github.javaparser.ast.body.MethodDeclaration;
|
||||||
|
import com.github.javaparser.ast.visitor.VoidVisitor;
|
||||||
|
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
|
||||||
|
import com.github.javaparser.utils.SourceRoot;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
if(args.length == 0) {
|
||||||
|
System.err.println("Should provide the path to the source code");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = new File(args[0]);
|
||||||
|
if(!file.exists() || !file.isDirectory() || !file.canRead()) {
|
||||||
|
System.err.println("Provide a path to an existing readable directory");
|
||||||
|
System.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceRoot root = new SourceRoot(file.toPath());
|
||||||
|
PublicElementsPrinter printer = new PublicElementsPrinter();
|
||||||
|
root.parse("", (localPath, absolutePath, result) -> {
|
||||||
|
result.ifSuccessful(unit -> unit.accept(printer, null));
|
||||||
|
return SourceRoot.Callback.Result.DONT_SAVE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package fr.istic.vv;
|
||||||
|
|
||||||
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
|
import com.github.javaparser.ast.body.*;
|
||||||
|
import com.github.javaparser.ast.visitor.VoidVisitorWithDefaults;
|
||||||
|
|
||||||
|
|
||||||
|
// This class visits a compilation unit and
|
||||||
|
// prints all public enum, classes or interfaces along with their public methods
|
||||||
|
public class PublicElementsPrinter extends VoidVisitorWithDefaults<Void> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CompilationUnit unit, Void arg) {
|
||||||
|
for(TypeDeclaration<?> type : unit.getTypes()) {
|
||||||
|
type.accept(this, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitTypeDeclaration(TypeDeclaration<?> declaration, Void arg) {
|
||||||
|
if(!declaration.isPublic()) return;
|
||||||
|
System.out.println(declaration.getFullyQualifiedName().orElse("[Anonymous]"));
|
||||||
|
for(MethodDeclaration method : declaration.getMethods()) {
|
||||||
|
method.accept(this, arg);
|
||||||
|
}
|
||||||
|
// Printing nested types in the top level
|
||||||
|
for(BodyDeclaration<?> member : declaration.getMembers()) {
|
||||||
|
if (member instanceof TypeDeclaration)
|
||||||
|
member.accept(this, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ClassOrInterfaceDeclaration declaration, Void arg) {
|
||||||
|
visitTypeDeclaration(declaration, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(EnumDeclaration declaration, Void arg) {
|
||||||
|
visitTypeDeclaration(declaration, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MethodDeclaration declaration, Void arg) {
|
||||||
|
if(!declaration.isPublic()) return;
|
||||||
|
System.out.println(" " + declaration.getDeclarationAsString(true, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
34
exercises/designer-help.md
Normal file
34
exercises/designer-help.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Use PMD designer
|
||||||
|
|
||||||
|
## Download JavaFX
|
||||||
|
If JavaFX is not installed you can download it [here](https://gluonhq.com/products/javafx/).
|
||||||
|
Check the current version of your JDK by running ```java --version``` and download the corresponding version ("Minimum JDK" column).
|
||||||
|
* Java 17 => JavaFX 21
|
||||||
|
* Java 11 => JavaDX 17
|
||||||
|
|
||||||
|
## Set up JavaFX
|
||||||
|
Before using `designer` you need to specify JavaFX path.
|
||||||
|
|
||||||
|
#### Linux/Unix
|
||||||
|
```shell
|
||||||
|
$ export JAVAFX_HOME=path/to/javafx-sdk-{version}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
```shell
|
||||||
|
$ set JAVAFX_HOME=path\to\javafx-sdk-{version}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Run designer
|
||||||
|
|
||||||
|
#### Linux/Unix
|
||||||
|
```shell
|
||||||
|
$ run.sh designer
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
```shell
|
||||||
|
$ pmd.bat designer
|
||||||
|
```
|
||||||
25
exercises/extending-pmd.md
Normal file
25
exercises/extending-pmd.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Extending PMD
|
||||||
|
|
||||||
|
Use XPath to define a new rule for PMD to prevent complex code. The rule should detect the use of three or more nested `if` statements in Java programs so it can detect patterns like the following:
|
||||||
|
|
||||||
|
```Java
|
||||||
|
if (...) {
|
||||||
|
...
|
||||||
|
if (...) {
|
||||||
|
...
|
||||||
|
if (...) {
|
||||||
|
....
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Notice that the nested `if`s may not be direct children of the outer `if`s. They may be written, for example, inside a `for` loop or any other statement.
|
||||||
|
Write below the XML definition of your rule.
|
||||||
|
|
||||||
|
You can find more information on extending PMD in the following link: https://pmd.github.io/latest/pmd_userdocs_extending_writing_rules_intro.html, as well as help for using `pmd-designer` [here](https://github.com/selabs-ur1/VV-ISTIC-TP2/blob/master/exercises/designer-help.md).
|
||||||
|
|
||||||
|
Use your rule with different projects and describe you findings below. See the [instructions](../sujet.md) for suggestions on the projects to use.
|
||||||
|
|
||||||
|
## Answer
|
||||||
|
|
||||||
9
exercises/jp-cc.md
Normal file
9
exercises/jp-cc.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Cyclomatic Complexity with JavaParser
|
||||||
|
|
||||||
|
With the help of JavaParser implement a program that computes the Cyclomatic Complexity (CC) of all methods in a given Java project. The program should take as input the path to the source code of the project. It should produce a report in the format of your choice (TXT, CSV, Markdown, HTML, etc.) containing a table showing for each method: the package and name of the declaring class, the name of the method, the types of the parameters and the value of CC.
|
||||||
|
Your application should also produce a histogram showing the distribution of CC values in the project. Compare the histogram of two or more projects.
|
||||||
|
|
||||||
|
|
||||||
|
Include in this repository the code of your application. Remove all unnecessary files like compiled binaries. Do include the reports and plots you obtained from different projects. See the [instructions](../sujet.md) for suggestions on the projects to use.
|
||||||
|
|
||||||
|
You may use [javaparser-starter](../code/javaparser-starter) as a starting point.
|
||||||
11
exercises/jp-tcc.md
Normal file
11
exercises/jp-tcc.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Class cohesion with JavaParser
|
||||||
|
|
||||||
|
With the help of JavaParser implement a program that computes the Tight Class Cohesion (TCC) for each class in a given Java project. The program should take as input the path to the source code of the project. It should produce a report in the format of your choice (TXT, CSV, Markdown, HTML, etc.) containing a table showing for each class: the package, name and TCC value.
|
||||||
|
Your application should also produce a histogram showing the distribution of CC values in the project. Compare the histogram of two or more projects.
|
||||||
|
Finally, your application should also produce the dependency graph of each class (cf. example [here](https://people.irisa.fr/Benoit.Combemale/pub/course/vv/vv-textbook-v0.1.pdf#cohesion-graph)). The graph should be written using the [GraphViz DOT format](https://www.graphviz.org/)
|
||||||
|
|
||||||
|
Ignore inherited members to compute TCC of a class.
|
||||||
|
|
||||||
|
Include in this repository the code of your application. Remove all unnecessary files like compiled binaries. Do include the reports and plots you obtained from different projects. See the [instructions](../sujet.md) for suggestions on the projects to use.
|
||||||
|
|
||||||
|
You may use [javaparser-starter](../code/javaparser-starter) as a starting point.
|
||||||
29
exercises/no-getter.md
Normal file
29
exercises/no-getter.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# No getter!
|
||||||
|
|
||||||
|
With the help of JavaParser implement a program that obtains the private fields of public classes that have no public getter in a Java project.
|
||||||
|
|
||||||
|
A field has a public getter if, in the same class, there is a public method that simply returns the value of the field and whose name is `get<name-of-the-field>`.
|
||||||
|
|
||||||
|
For example, in the following class:
|
||||||
|
|
||||||
|
```Java
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
private int age;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public boolean isAdult() {
|
||||||
|
return age > 17;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`name` has a public getter, while `age` doesn't.
|
||||||
|
|
||||||
|
The program should take as input the path to the source code of the project. It should produce a report in the format of your choice (TXT, CSV, Markdown, HTML, etc.) that lists for each detected field: its name, the name of the declaring class and the package of the declaring class.
|
||||||
|
|
||||||
|
Include in this repository the code of your application. Remove all unnecessary files like compiled binaries. See the [instructions](../sujet.md) for suggestions on the projects to use.
|
||||||
|
|
||||||
|
*Disclaimer* In a real project not all fields need to be accessed with a public getter.
|
||||||
18
exercises/pmd-help.md
Normal file
18
exercises/pmd-help.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# PMD
|
||||||
|
|
||||||
|
### Requirement
|
||||||
|
* Java 8 or above
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
* download latest release https://github.com/pmd/pmd/releases/download/pmd_releases%2F7.5.0/pmd-dist-7.5.0-bin.zip
|
||||||
|
* unzip
|
||||||
|
* add the bin folder to your path
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
(Instruction for linux use ```pmd.bat``` for windows)
|
||||||
|
* Do not hesitate to have a look on the help page ```pmd -h```
|
||||||
|
* Same thing for the check help page```pmd check -h```
|
||||||
|
* Run the analysis ```pmd check -f text -R rulesets/java/quickstart.xml -d [PROJECT_PATH] -r [REPORT_FILE_PATH]```
|
||||||
|
|
||||||
|
|
||||||
|
For more information consult the instructions given in https://pmd.github.io/pmd/pmd_userdocs_installation.html
|
||||||
8
exercises/tcc-vs-lcc.md
Normal file
8
exercises/tcc-vs-lcc.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# TCC *vs* LCC
|
||||||
|
|
||||||
|
Explain under which circumstances *Tight Class Cohesion* (TCC) and *Loose Class Cohesion* (LCC) metrics produce the same value for a given Java class. Build an example of such as class and include the code below or find one example in an open-source project from Github and include the link to the class below. Could LCC be lower than TCC for any given class? Explain.
|
||||||
|
|
||||||
|
A refresher on TCC and LCC is available in the [course notes](https://oscarlvp.github.io/vandv-classes/#cohesion-graph).
|
||||||
|
|
||||||
|
## Answer
|
||||||
|
|
||||||
6
exercises/using-pmd.md
Normal file
6
exercises/using-pmd.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Using PMD
|
||||||
|
|
||||||
|
Pick a Java project from Github (see the [instructions](../sujet.md) for suggestions). Run PMD on its source code using any ruleset (see the [pmd install instruction](./pmd-help.md)). Describe below an issue found by PMD that you think should be solved (true positive) and include below the changes you would add to the source code. Describe below an issue found by PMD that is not worth solving (false positive). Explain why you would not solve this issue.
|
||||||
|
|
||||||
|
## Answer
|
||||||
|
|
||||||
44
pmd-designer-docker/Dockerfile
Normal file
44
pmd-designer-docker/Dockerfile
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Use an official OpenJDK base image
|
||||||
|
FROM openjdk:23-jdk-slim
|
||||||
|
|
||||||
|
# Install necessary dependencies (e.g., X11 libraries for GUI)
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libx11-dev \
|
||||||
|
libxext-dev \
|
||||||
|
libxrender-dev \
|
||||||
|
libxtst-dev \
|
||||||
|
libxi6 \
|
||||||
|
xauth \
|
||||||
|
libx11-xcb1 \
|
||||||
|
libxrandr2 \
|
||||||
|
libxrender1 \
|
||||||
|
libxxf86vm1 \
|
||||||
|
libgtk-3-0 \
|
||||||
|
libgl1-mesa-glx \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
|
# Download and install OpenJFX 23.0.1 SDK for Linux
|
||||||
|
RUN wget https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-x64_bin-sdk.zip -O /tmp/openjfx.zip \
|
||||||
|
&& mkdir -p /opt/openjfx \
|
||||||
|
&& unzip /tmp/openjfx.zip -d /opt/openjfx \
|
||||||
|
&& rm /tmp/openjfx.zip
|
||||||
|
|
||||||
|
# Download and install PMD 7.7.0
|
||||||
|
RUN wget https://github.com/pmd/pmd/releases/download/pmd_releases%2F7.7.0/pmd-dist-7.7.0-bin.zip -O /tmp/pmd.zip \
|
||||||
|
&& mkdir -p /opt/pmd \
|
||||||
|
&& unzip /tmp/pmd.zip -d /opt/pmd \
|
||||||
|
&& rm /tmp/pmd.zip
|
||||||
|
|
||||||
|
# Set environment variables for Java, OpenJFX, and PMD
|
||||||
|
ENV JAVA_HOME=/usr/lib/jvm/java-23-openjdk
|
||||||
|
ENV PATH=$JAVA_HOME/bin:$PATH
|
||||||
|
ENV JAVAFX_HOME=/opt/openjfx/javafx-sdk-23.0.1
|
||||||
|
ENV PMD_HOME=/opt/pmd/pmd-bin-7.7.0
|
||||||
|
|
||||||
|
# Expose necessary ports for the X server (if running on a host machine)
|
||||||
|
EXPOSE 6000
|
||||||
|
|
||||||
|
# Command to run PMD Designer with OpenJFX 23.0.1
|
||||||
|
CMD ["/opt/pmd/pmd-bin-7.7.0/bin/pmd", "designer"]
|
||||||
12
pmd-designer-docker/README.md
Normal file
12
pmd-designer-docker/README.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Command to run :
|
||||||
|
```
|
||||||
|
docker build -t pmd-designer-javafx:7.7.0 .
|
||||||
|
xhost +local:docker
|
||||||
|
docker run -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix pmd-designer-javafx:7.7.0
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
For windows :
|
||||||
|
```
|
||||||
|
docker run -e DISPLAY=host.docker.internal:0 pmd-designer-javafx:7.7.0
|
||||||
|
```
|
||||||
33
sujet.md
Normal file
33
sujet.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Static analysis
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
Some of the exercises in this practical session use PMD, JavaParser or require a full project as input.
|
||||||
|
|
||||||
|
To obtain and use PMD, consult the instructions given in https://pmd.github.io/pmd/pmd_userdocs_installation.html
|
||||||
|
|
||||||
|
The folder [javaparser-starter](code/javaparser-starter) contains the code of an application that uses JavaParser to print all public classes and public methods from a given project. You can use this example as a starting point for all exercises using JavaParser.
|
||||||
|
|
||||||
|
We recommend you use the following projects as input for the exercises:
|
||||||
|
|
||||||
|
- [Apache Commons Collections](https://github.com/apache/commons-collections)
|
||||||
|
- [Apache Commons CLI](https://github.com/apache/commons-cli)
|
||||||
|
- [Apache Commons Math](https://github.com/apache/commons-math)
|
||||||
|
- [Apache Commons Lang](https://github.com/apache/commons-lang)
|
||||||
|
|
||||||
|
Feel free to use any other project you want.
|
||||||
|
|
||||||
|
## Exercises
|
||||||
|
|
||||||
|
1. [TCC *vs* LCC](exercises/tcc-vs-lcc.md)
|
||||||
|
|
||||||
|
2. [Using PMD](exercises/using-pmd.md)
|
||||||
|
|
||||||
|
3. [Extending PMD](exercises/extending-pmd.md)
|
||||||
|
|
||||||
|
4. [No getter!](exercises/no-getter.md)
|
||||||
|
|
||||||
|
5. [Cyclomatic Complexity with JavaParser](exercises/jp-cc.md)
|
||||||
|
|
||||||
|
6. [Class cohesion with JavaParser](exercises/jp-tcc.md) (bonus)
|
||||||
|
|
||||||
Reference in New Issue
Block a user