浏览代码

Merge pull request #346 from dfuchss/feature/java-17

Update to Java 17, thus allowing the Java frontend to fully parse Java 17 code.
Timur Sağlam 4 年之前
父节点
当前提交
ce92f80971

+ 2 - 2
.github/workflows/maven.yml

@@ -18,8 +18,8 @@ jobs:
     - name: Set up JDK
       uses: actions/setup-java@v2
       with:
-        java-version: 14
-        distribution: 'zulu'
+        java-version: 17
+        distribution: 'temurin'
             
     - name: Run Tests
       run: mvn test -B

+ 2 - 2
.github/workflows/publish.yml

@@ -11,8 +11,8 @@ jobs:
       - name: Set up Maven Central Repository
         uses: actions/setup-java@v2
         with:
-          java-version: '14'
-          distribution: 'zulu'
+          java-version: '17'
+          distribution: 'temurin'
       - name: Set maven settings.xml
         uses: whelk-io/maven-settings-xml-action@v20
         with:

+ 2 - 2
.github/workflows/spotless.yml

@@ -18,8 +18,8 @@ jobs:
     - name: Set up JDK
       uses: actions/setup-java@v2
       with:
-        java-version: 14
-        distribution: 'zulu'
+        java-version: 17
+        distribution: 'temurin'
       
     - name: Check with Spotless
       run: mvn clean spotless:check

+ 8 - 1
jplag.frontend.java/src/main/java/de/jplag/java/JavaToken.java

@@ -141,9 +141,16 @@ public class JavaToken extends Token implements JavaTokenConstants {
                 return "PROVIDES";
             case J_REQUIRES:
                 return "REQUIRES";
-
             case J_TRY_WITH_RESOURCE:
                 return "TRY_RES";
+            case J_YIELD:
+                return "YIELD";
+            case J_DEFAULT:
+                return "DEFAULT";
+            case J_RECORD_BEGIN:
+                return "RECORD{";
+            case J_RECORD_END:
+                return "}RECORD";
 
             default:
                 System.err.println("UNKNOWN: " + type);

+ 9 - 1
jplag.frontend.java/src/main/java/de/jplag/java/JavaTokenConstants.java

@@ -76,5 +76,13 @@ public interface JavaTokenConstants extends TokenConstants {
     int J_MODULE_BEGIN = 64;       // check
     int J_MODULE_END = 65;        // check
 
-    int NUM_DIFF_TOKENS = 66;
+    // new in 13
+    final static int J_YIELD = 66;
+
+    // new in 17
+    final static int J_DEFAULT = 67;
+    final static int J_RECORD_BEGIN = 68;
+    final static int J_RECORD_END = 69;
+
+    final static int NUM_DIFF_TOKENS = 70;	    // @formatter:on
 }

+ 37 - 22
jplag.frontend.java/src/main/java/de/jplag/java/TokenGeneratingTreeScanner.java

@@ -11,6 +11,7 @@ import com.sun.source.tree.ClassTree;
 import com.sun.source.tree.CompilationUnitTree;
 import com.sun.source.tree.ConditionalExpressionTree;
 import com.sun.source.tree.ContinueTree;
+import com.sun.source.tree.DefaultCaseLabelTree;
 import com.sun.source.tree.DoWhileLoopTree;
 import com.sun.source.tree.EnhancedForLoopTree;
 import com.sun.source.tree.ErroneousTree;
@@ -37,6 +38,7 @@ import com.sun.source.tree.TryTree;
 import com.sun.source.tree.TypeParameterTree;
 import com.sun.source.tree.VariableTree;
 import com.sun.source.tree.WhileLoopTree;
+import com.sun.source.tree.YieldTree;
 import com.sun.source.util.SourcePositions;
 import com.sun.source.util.TreeScanner;
 
@@ -67,35 +69,32 @@ final class TokenGeneratingTreeScanner extends TreeScanner<Object, Object> {
 
     @Override
     public Object visitClass(ClassTree node, Object p) {
-        boolean isEnum = false;
-        boolean isInterface = false;
-        boolean isAnnotation = false;
         long n = positions.getStartPosition(ast, node);
-        long m = positions.getEndPosition(ast, node);
+        long m = positions.getEndPosition(ast, node) - 1;
 
-        if (node.getKind() == Tree.Kind.ENUM)
-            isEnum = true;
-        if (node.getKind() == Tree.Kind.INTERFACE)
-            isInterface = true;
-        if (node.getKind() == Tree.Kind.ANNOTATION_TYPE)
-            isAnnotation = true;
-        if (isEnum)
+        if (node.getKind() == Tree.Kind.ENUM) {
             parser.add(JavaTokenConstants.J_ENUM_BEGIN, filename, map.getLineNumber(n), map.getColumnNumber(n), 4);
-        if (isInterface)
+        } else if (node.getKind() == Tree.Kind.INTERFACE) {
             parser.add(JavaTokenConstants.J_INTERFACE_BEGIN, filename, map.getLineNumber(n), map.getColumnNumber(n), 9);
-        if (isAnnotation)
+        } else if (node.getKind() == Tree.Kind.RECORD) {
+            parser.add(JavaTokenConstants.J_RECORD_BEGIN, filename, map.getLineNumber(n), map.getColumnNumber(n), 1);
+        } else if (node.getKind() == Tree.Kind.ANNOTATION_TYPE) {
             parser.add(JavaTokenConstants.J_ANNO_T_BEGIN, filename, map.getLineNumber(n), map.getColumnNumber(n), 10);
-        if (!isEnum && !isInterface && !isAnnotation)
+        } else if (node.getKind() == Tree.Kind.CLASS) {
             parser.add(JavaTokenConstants.J_CLASS_BEGIN, filename, map.getLineNumber(n), map.getColumnNumber(n), 5);
+        }
         Object result = super.visitClass(node, p);
-        if (!isEnum && !isInterface && !isAnnotation)
-            parser.add(JavaTokenConstants.J_CLASS_END, filename, map.getLineNumber(m - 1), map.getColumnNumber(m - 1), 1);
-        if (isAnnotation)
-            parser.add(JavaTokenConstants.J_ANNO_T_END, filename, map.getLineNumber(m - 1), map.getColumnNumber(m - 1), 1);
-        if (isEnum)
-            parser.add(JavaTokenConstants.J_ENUM_END, filename, map.getLineNumber(m - 1), map.getColumnNumber(m - 1), 1);
-        if (isInterface)
-            parser.add(JavaTokenConstants.J_INTERFACE_END, filename, map.getLineNumber(m - 1), map.getColumnNumber(m - 1), 1);
+        if (node.getKind() == Tree.Kind.ENUM) {
+            parser.add(JavaTokenConstants.J_ENUM_END, filename, map.getLineNumber(m), map.getColumnNumber(m), 1);
+        } else if (node.getKind() == Tree.Kind.INTERFACE) {
+            parser.add(JavaTokenConstants.J_INTERFACE_END, filename, map.getLineNumber(m), map.getColumnNumber(m), 1);
+        } else if (node.getKind() == Tree.Kind.RECORD) {
+            parser.add(JavaTokenConstants.J_RECORD_END, filename, map.getLineNumber(m), map.getColumnNumber(m), 1);
+        } else if (node.getKind() == Tree.Kind.ANNOTATION_TYPE) {
+            parser.add(JavaTokenConstants.J_ANNO_T_END, filename, map.getLineNumber(m), map.getColumnNumber(m), 1);
+        } else if (node.getKind() == Tree.Kind.CLASS) {
+            parser.add(JavaTokenConstants.J_CLASS_END, filename, map.getLineNumber(m), map.getColumnNumber(m), 1);
+        }
         return result;
     }
 
@@ -377,4 +376,20 @@ final class TokenGeneratingTreeScanner extends TreeScanner<Object, Object> {
         parser.errorsInc();
         return super.visitErroneous(node, p);
     }
+
+    @Override
+    public Object visitYield(YieldTree node, Object p) {
+        long start = positions.getStartPosition(ast, node);
+        long end = positions.getEndPosition(ast, node);
+        parser.add(JavaTokenConstants.J_YIELD, filename, map.getLineNumber(start), map.getColumnNumber(start), (end - start));
+        return super.visitYield(node, p);
+    }
+
+    @Override
+    public Object visitDefaultCaseLabel(DefaultCaseLabelTree node, Object p) {
+        long start = positions.getStartPosition(ast, node);
+        long end = positions.getEndPosition(ast, node);
+        parser.add(JavaTokenConstants.J_DEFAULT, filename, map.getLineNumber(start), map.getColumnNumber(start), (end - start));
+        return super.visitDefaultCaseLabel(node, p);
+    }
 }

+ 14 - 7
jplag/src/test/java/de/jplag/NewJavaFeaturesTest.java

@@ -7,21 +7,28 @@ import org.junit.Test;
 import de.jplag.exceptions.ExitException;
 
 public class NewJavaFeaturesTest extends TestBase {
+    private static final int EXPECTED_MATCHES = 6; // might change if you add files to the submissions
+    private static final double EXPECTED_SIMILARITY = 96.0; // 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";
+    private static final String CHANGE_MESSAGE = "Number of %s changed! If intended, modify the test case!";
 
     @Test
     public void testJavaFeatureDuplicates() throws ExitException {
         JPlagResult result = runJPlagWithExclusionFile(ROOT_DIRECTORY, EXCLUSION_FILE_NAME);
 
-        // Two submissions, one comparison with 100% match:
-        assertEquals(2, result.getNumberOfSubmissions());
-        assertEquals(1, result.getComparisons().size());
-        var comparison = result.getComparisons().get(0);
-        assertEquals(92.1, comparison.similarity(), DELTA);
+        // Ensure test input did not change:
+        assertEquals(String.format(CHANGE_MESSAGE, "Submissions"), 2, result.getNumberOfSubmissions());
+        for (Submission submission : result.getSubmissions().getSubmissions()) {
+            assertEquals(String.format(CHANGE_MESSAGE, "Files"), 6, submission.getFiles().size());
+        }
+        assertEquals(String.format(CHANGE_MESSAGE, "Comparisons"), 1, result.getComparisons().size());
 
-        // Five matches that each start at the same position:
-        assertEquals(4, comparison.getMatches().size());
+        // Check similarity and number of matches:
+        var comparison = result.getComparisons().get(0);
+        assertEquals(EXPECTED_SIMILARITY, comparison.similarity(), DELTA);
+        assertEquals(EXPECTED_MATCHES, comparison.getMatches().size());
     }
 
 }

+ 42 - 0
jplag/src/test/resources/de/jplag/samples/NewJavaFeatures/A/Java17.java

@@ -0,0 +1,42 @@
+public class Java17 {
+
+	public Java17() {
+		Shape s = Math.random() > 0.5 ? new Circle(42) : new Rect(1, 4);
+		Shape c = new Circle(432);
+		Shape r = new Rect(1, 2);
+		double area = s.area() + c.area() + r.area();
+		System.out.println(area);
+	}
+
+	private abstract static sealed class Shape permits Circle, Rect {
+		public abstract double area();
+	}
+
+	private static final class Circle extends Shape {
+		private double r;
+
+		private Circle(double r) {
+			this.r = r;
+		}
+
+		@Override
+		public double area() {
+			return 2 * Math.PI * r * r;
+		}
+	}
+
+	private static final class Rect extends Shape {
+		private final int a;
+		private final int b;
+
+		private Rect(int a, int b) {
+			this.a = a;
+			this.b = b;
+		}
+
+		@Override
+		public double area() {
+			return a * b;
+		}
+	}
+}

+ 44 - 0
jplag/src/test/resources/de/jplag/samples/NewJavaFeatures/A/Java17Preview.java

@@ -0,0 +1,44 @@
+public class Java17Preview {
+
+	public Java17Preview() {
+		Shape s = Math.random() > 0.5 ? new Circle(42) : new Rect(1, 4);
+		double area = switch (s) {
+			case Circle c -> c.area() + 0.123;
+			case Rect r -> r.area() + r.area();
+		};
+
+		System.out.println(area);
+	}
+
+	private abstract static sealed class Shape permits Circle, Rect {
+		public abstract double area();
+	}
+
+	private static final class Circle extends Shape {
+		private double r;
+
+		private Circle(double r) {
+			this.r = r;
+		}
+
+		@Override
+		public double area() {
+			return 2 * Math.PI * r * r;
+		}
+	}
+
+	private static final class Rect extends Shape {
+		private final int a;
+		private final int b;
+
+		private Rect(int a, int b) {
+			this.a = a;
+			this.b = b;
+		}
+
+		@Override
+		public double area() {
+			return a * b;
+		}
+	}
+}

+ 42 - 0
jplag/src/test/resources/de/jplag/samples/NewJavaFeatures/B/Java17.java

@@ -0,0 +1,42 @@
+public class Java17 {
+
+	public Java17() {
+		Shape s = Math.random() > 0.6 ? new Circle(5435) : new Rect(54, 4);
+		Shape c = new Circle(43425);
+		Shape r = new Rect(14, 21);
+		double area = s.area() + c.area() + r.area();
+		System.out.println(area);
+	}
+
+	private abstract static sealed class Shape permits Circle, Rect {
+		public abstract double area();
+	}
+
+	private static final class Circle extends Shape {
+		private double r;
+
+		private Circle(double r) {
+			this.r = r;
+		}
+
+		@Override
+		public double area() {
+			return 2 * Math.PI * r * r;
+		}
+	}
+
+	private static final class Rect extends Shape {
+		private final int a;
+		private final int b;
+
+		private Rect(int a, int b) {
+			this.a = a;
+			this.b = b;
+		}
+
+		@Override
+		public double area() {
+			return a * b;
+		}
+	}
+}

+ 44 - 0
jplag/src/test/resources/de/jplag/samples/NewJavaFeatures/B/Java17Preview.java

@@ -0,0 +1,44 @@
+public class Java17Preview {
+
+	public Java17Preview() {
+		Shape s = Math.random() > 0.5 ? new Circle(24) : new Rect(5, 1);
+		double area = switch (s) {
+			case Circle c -> c.area() + 2.345;
+			case Rect r -> r.area() + r.area();
+		};
+
+		System.out.println(area);
+	}
+
+	private abstract static sealed class Shape permits Circle, Rect {
+		public abstract double area();
+	}
+
+	private static final class Circle extends Shape {
+		private double r;
+
+		private Circle(double r) {
+			this.r = r;
+		}
+
+		@Override
+		public double area() {
+			return 2 * Math.PI * r * r;
+		}
+	}
+
+	private static final class Rect extends Shape {
+		private final int a;
+		private final int b;
+
+		private Rect(int a, int b) {
+			this.a = a;
+			this.b = b;
+		}
+
+		@Override
+		public double area() {
+			return a * b;
+		}
+	}
+}

+ 1 - 1
jplag/src/test/resources/de/jplag/samples/NewJavaFeatures/blacklist.txt

@@ -1 +1 @@
-Java16.java
+Java17Preview.java

+ 2 - 2
pom.xml

@@ -79,8 +79,8 @@
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <maven.compiler.source>14</maven.compiler.source>
-        <maven.compiler.target>14</maven.compiler.target>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
         <spotless.version>2.22.1</spotless.version>
     </properties>
     <dependencyManagement>