Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/complete-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- uses: actions/setup-node@v5
Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- name: Get JAR
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- uses: actions/setup-node@v5
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
java-version: '21'
java-version: '25'
distribution: 'temurin'
- name: Set maven settings.xml
uses: whelk-io/maven-settings-xml-action@v22
Expand All @@ -29,7 +29,7 @@ jobs:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
java-version: '21'
java-version: '25'
distribution: 'temurin'
- uses: actions/setup-node@v5
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/report-viewer-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- name: Build Assembly
Expand All @@ -41,7 +41,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- name: Get JAR
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/report-viewer-dev-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- name: Build Assembly
Expand All @@ -41,7 +41,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- name: Get JAR
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
java-version: 25

- name: Cache SonarCloud packages
uses: actions/cache@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
java-version: 25

- name: Cache SonarCloud packages
uses: actions/cache@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/spotless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- name: Check with Spotless
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/verify-help-text.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: 21
java-version: 25
distribution: 'temurin'

- name: Build Assembly
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
[![License](https://img.shields.io/github/license/jplag/jplag.svg)](https://github.com/jplag/jplag/blob/main/LICENSE)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/jplag/JPlag)](https://github.com/jplag/JPlag/pulse)
[![SonarCloud Coverage](https://sonarcloud.io/api/project_badges/measure?project=jplag_JPlag&metric=coverage)](https://sonarcloud.io/component_measures?metric=Coverage&view=list&id=jplag_JPlag)
[![Java Version](https://img.shields.io/badge/java-SE%2021-yellowgreen)](#download-and-installation)
[![Java Version](https://img.shields.io/badge/java-SE%2025-yellowgreen)](#download-and-installation)


JPlag finds pairwise similarities among a set of multiple programs. It can reliably detect software plagiarism and collusion in software development, even when obfuscated. All similarities are calculated locally; no source code or plagiarism results are ever uploaded online. JPlag supports a large number of languages.
Expand All @@ -29,7 +29,7 @@ All supported languages and their supported versions are listed below.

| Language | Version | CLI Argument Name | [state](https://github.com/jplag/JPlag/wiki/2.-Supported-Languages) | parser |
|--------------------------------------------------------|---------------------------------------------------------------------------------------:|-------------------|:-------------------------------------------------------------------:|:---------:|
| [Java](https://www.java.com) | 21 | java | mature | JavaC |
| [Java](https://www.java.com) | 25 | java | mature | JavaC |
| [C](https://isocpp.org) | 11 | c | legacy | JavaCC |
| [C++](https://isocpp.org) | 14 | cpp | mature | ANTLR 4 |
| [C#](https://docs.microsoft.com/en-us/dotnet/csharp/) | 6 | csharp | mature | ANTLR 4 |
Expand All @@ -51,7 +51,7 @@ All supported languages and their supported versions are listed below.
| Multi-Language | - | multi | alpha | - |

## Download and Installation
You need Java SE 21 to run or build JPlag.
You need Java SE 25 to run or build JPlag.

### Downloading a release
* Download a [released version](https://github.com/jplag/jplag/releases).
Expand Down
18 changes: 16 additions & 2 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<dependency>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder-processor</artifactId>
<version>48</version>
<version>${record.builder.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand All @@ -57,11 +57,25 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
<version>${maven.javadoc.plugin.version}</version>
<configuration>
<sourcepath>src/main/java;target/generated-sources/annotations</sourcepath>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.soabase.record-builder</groupId>
<artifactId>record-builder-processor</artifactId>
<version>${record.builder.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
16 changes: 12 additions & 4 deletions core/src/test/java/de/jplag/NewJavaFeaturesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.jplag.exceptions.ExitException;

Expand All @@ -15,9 +17,11 @@
*/
class NewJavaFeaturesTest extends TestBase {

private static final int EXPECTED_MATCHES = 8; // might change if you add files to the submissions
private static final int NUMBER_OF_TEST_FILES = 8;
private static final double EXPECTED_SIMILARITY = 0.971; // might change if you add files to the submissions
private final Logger logger = LoggerFactory.getLogger(this.getClass());

private static final int EXPECTED_MATCHES = 9; // might change if you add files to the submissions
private static final int NUMBER_OF_TEST_FILES = 10;
private static final double EXPECTED_SIMILARITY = 0.898; // might change if you add files to the submissions

private static final String EXCLUSION_FILE_NAME = "blacklist.txt";
private static final String ROOT_DIRECTORY = "NewJavaFeatures";
Expand All @@ -26,7 +30,7 @@ class NewJavaFeaturesTest extends TestBase {
private static final String VERSION_MATCH_MESSAGE = "Java version matches, but results deviate from expected values";
private static final String CI_VARIABLE = "CI";

private static final int EXPECTED_JAVA_VERSION = 21;
private static final int EXPECTED_JAVA_VERSION = 25;

@Test
@DisplayName("test comparison of Java files with modern language features")
Expand All @@ -38,6 +42,10 @@ void testJavaFeatureDuplicates() throws ExitException {

JPlagResult result = runJPlagWithExclusionFile(ROOT_DIRECTORY, EXCLUSION_FILE_NAME);

for (Submission submission : result.getSubmissions().getSubmissions()) {
logger.info(TokenPrinter.printTokens(submission.getTokenList(), submission.getRoot()));
}

// Ensure test input did not change:
assertEquals(2, result.getNumberOfSubmissions(), String.format(CHANGE_MESSAGE, "Submissions"));
for (Submission submission : result.getSubmissions().getSubmissions()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void main() {
case Both(Circle c, Rect(int w, int _)) -> System.out.println("something");
case Square -> {
int l = ((Square) s).length;
System.out.println(STR."Square with length \{Math.abs(l)}");
System.out.println("Square with length " + Math.abs(l));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import module java.base;
import module java.sql;

public class Java25 {

// Compact main method
void main() {
// Module imports:
List<String> list = List.of("a", "b", "c"); // java.util.List from java.base is imported
java.sql.Date date = new java.sql.Date(0); // java.sql.Date from java.sql is imported

// Unnamed variables:
int count;
for (String _ : list) {// loop variable is unused
count++;
}

}

class Person {
Person(int age) {
/* ... */ }
}

class Employee extends Person {
Employee(int age) {
if (age < 0)
age = 0; // validate before calling super
super(age); // now allowed after other statements
IO.println("Employee age: " + age);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import module java.base;
import module java.sql;

// Compact main method
void main() {
foo();
}

private static void foo() {
IO.println("Compact source file and main");
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void main() {
case Both(Circle c, Rect(int _, int w)) -> System.out.println("something");
case Square -> {
int l = ((Square) s).length;
System.out.println(STR."Square with length \{Math.abs(l)}");
System.out.println("Square with length " + Math.abs(l));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import java.util.List;
import java.sql.Date;

public class Java25 {

// No compact main method
public static void main(String[] args) {
// No module imports:
List<String> list = List.of("a", "b", "c"); // java.util.List from java.base is imported
java.sql.Date date = new java.sql.Date(0); // java.sql.Date from java.sql is imported

// No unnamed variables:
int count;
for (String text : list) {// loop variable is unused
count++;
}

}

class Person {
Person(int age) {
}
}

class Employee extends Person {
Employee(int age) {
if (age < 0) {
age = 0; // validate before calling super
}
super(age); // now allowed after other statements
System.out.println("Employee age: " + age);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import java.util.List;
import java.sql.Date;

public class Java25Compact {

// No compact main method
public static void main(String[] args) {
foo();
}

private static void foo() {
IO.println("No compact source file and main!");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ public Void visitClass(ClassTree node, Void unused) {
variableRegistry.registerVariable(name, VariableScope.CLASS, mutable);
}
}
boolean hasModifiers = !node.getModifiers().getFlags().isEmpty();
long start = hasModifiers ? positions.getEndPosition(ast, node.getModifiers()) + 1 : positions.getStartPosition(ast, node);
long nameLength = node.getSimpleName().length();
long end = positions.getEndPosition(ast, node) - 1;

long start = extractStartPosition(node);
long end = extractEndPosition(node, start);

CodeSemantics semantics = CodeSemantics.createControl();
if (node.getKind() == Tree.Kind.ENUM) {
addToken(JavaTokenType.J_ENUM_BEGIN, start, 4, semantics);
Expand All @@ -150,6 +150,7 @@ public Void visitClass(ClassTree node, Void unused) {
} else if (node.getKind() == Tree.Kind.RECORD) {
addToken(JavaTokenType.J_RECORD_BEGIN, start, 1, semantics);
} else if (node.getKind() == Tree.Kind.ANNOTATION_TYPE) {
long nameLength = node.getSimpleName().length();
// The start position for the is calculated that way, because the @ is the final element in the modifier list for
// annotations
addToken(JavaTokenType.J_ANNO_T_BEGIN, start - 2, start - 2 + 11 + nameLength, semantics);
Expand All @@ -174,6 +175,24 @@ public Void visitClass(ClassTree node, Void unused) {
return null;
}

private long extractEndPosition(ClassTree node, long start) {
long end = positions.getEndPosition(ast, node) - 1;
if (end <= start) { // Java 25 compact source files have implicit classes
// use end of last member as class end:
return node.getMembers().stream().mapToLong(it -> positions.getEndPosition(ast, it)).max().orElse(start);
}
return end;
}

private long extractStartPosition(ClassTree node) {
boolean hasModifiers = !node.getModifiers().getFlags().isEmpty();
long endPosition = positions.getEndPosition(ast, node.getModifiers());
if (hasModifiers && endPosition != -1) { // Java 25 compact source files have implicit (final) classes.
return endPosition + 1;
}
return positions.getStartPosition(ast, node);
}

@Override
public Void visitImport(ImportTree node, Void unused) {
long start = positions.getStartPosition(ast, node);
Expand Down
Loading
Loading