From f6b0ee3b398ae16cf1d8ec0b22dc11ac3965f473 Mon Sep 17 00:00:00 2001
From: parinaz-st
Date: Fri, 17 Apr 2026 14:54:05 +0330
Subject: [PATCH 1/5] Study CodeTracker- Unit Tests Added case EXTRACT_FIXTURE
Temporarily commented-out (not supported on RM 3.0.11)
---
pom.xml | 19 +-
.../AnnotationTrackerChangeHistory.java | 2 +-
.../BlockTrackerChangeHistory.java | 2 +-
.../CommentTrackerChangeHistory.java | 2 +-
src/main/java/org/codetracker/Main.java | 224 ++++++++++--------
.../MethodTrackerChangeHistory.java | 2 +-
.../VariableTrackerChangeHistory.java | 2 +-
.../codetracker/AttributeTrackerImplTest.java | 58 +++++
src/test/java/org/codetracker/BlameTest.java | 122 ++++++++++
.../org/codetracker/BlockTrackerImplTest.java | 118 +++++++++
.../FalseRenameDetectionTests.java | 170 +++++++++++++
.../codetracker/MethodTrackerImplTest.java | 218 +++++++++++++++++
.../codetracker/VariableTrackerImplTest.java | 101 ++++++++
13 files changed, 935 insertions(+), 105 deletions(-)
create mode 100644 src/test/java/org/codetracker/AttributeTrackerImplTest.java
create mode 100644 src/test/java/org/codetracker/BlameTest.java
create mode 100644 src/test/java/org/codetracker/BlockTrackerImplTest.java
create mode 100644 src/test/java/org/codetracker/FalseRenameDetectionTests.java
create mode 100644 src/test/java/org/codetracker/MethodTrackerImplTest.java
create mode 100644 src/test/java/org/codetracker/VariableTrackerImplTest.java
diff --git a/pom.xml b/pom.xml
index 9ad2dcfd0f6..9776d1d153e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,10 +40,22 @@
refactoring-miner
${rm.version}
+
+
+
+
+
+
- junit
- junit
- 4.13.2
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.8.2
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.8.2
test
@@ -130,7 +142,6 @@
jackson-databind
2.17.1
-
diff --git a/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java b/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java
index a7f908e5abb..947b7eea6ab 100644
--- a/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java
+++ b/src/main/java/org/codetracker/AnnotationTrackerChangeHistory.java
@@ -140,7 +140,7 @@ public boolean checkForExtractionOrInline(Version currentVersion, Version parent
}
break;
}
- case EXTRACT_FIXTURE:
+// case EXTRACT_FIXTURE:TODO: upgrade RM version then uncomment
case MOVE_CODE: {
MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring;
Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion);
diff --git a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
index bf01484a079..8840829964c 100644
--- a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
+++ b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
@@ -441,7 +441,7 @@ else if (mapping instanceof LeafMapping && mapping.getFragment1() instanceof Sta
break;
}
*/
- case EXTRACT_FIXTURE:
+// case EXTRACT_FIXTURE: TODO: upgrade RM version then uncomment
case MOVE_CODE: {
MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring;
Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion);
diff --git a/src/main/java/org/codetracker/CommentTrackerChangeHistory.java b/src/main/java/org/codetracker/CommentTrackerChangeHistory.java
index 45c85d99b09..bb399aa000d 100644
--- a/src/main/java/org/codetracker/CommentTrackerChangeHistory.java
+++ b/src/main/java/org/codetracker/CommentTrackerChangeHistory.java
@@ -317,7 +317,7 @@ public boolean checkForExtractionOrInline(Version currentVersion, Version parent
}
break;
}
- case EXTRACT_FIXTURE:
+// case EXTRACT_FIXTURE:TODO: upgrade RM version then uncomment
case MOVE_CODE: {
MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring;
Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion);
diff --git a/src/main/java/org/codetracker/Main.java b/src/main/java/org/codetracker/Main.java
index 15293045808..86516fdde9d 100644
--- a/src/main/java/org/codetracker/Main.java
+++ b/src/main/java/org/codetracker/Main.java
@@ -16,51 +16,23 @@
import java.time.ZoneOffset;
public class Main {
- private final static String FOLDER_TO_CLONE = "tmp/";
+// private final static String FOLDER_TO_CLONE = "tmp/";
+ private final static String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting";
+ private final static String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git";
+
public static void main(String args[]) throws Exception {
GitService gitService = new GitServiceImpl();
- // BLOCK TRACKING EXAMPLE
- try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
- "https://github.com/checkstyle/checkstyle.git")){
-
- BlockTracker blockTracker = CodeTracker.blockTracker()
- .repository(repository)
- .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
- .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
- .methodName("fireErrors")
- .methodDeclarationLineNumber(384)
- .codeElementType(CodeElementType.ENHANCED_FOR_STATEMENT)
- .blockStartLineNumber(391)
- .blockEndLineNumber(393)
- .build();
-
- History blockHistory = blockTracker.track();
-
- for (History.HistoryInfo historyInfo : blockHistory.getHistoryInfoList()) {
- System.out.println("======================================================");
- System.out.println("Commit ID: " + historyInfo.getCommitId());
- System.out.println("Date: " +
- LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
- System.out.println("Before: " + historyInfo.getElementBefore().getName());
- System.out.println("After: " + historyInfo.getElementAfter().getName());
-
- for (Change change : historyInfo.getChangeList()) {
- System.out.println(change.getType().getTitle() + ": " + change);
- }
- }
- System.out.println("======================================================");
- }
// METHOD TRACKING EXAMPLE
- try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
- "https://github.com/checkstyle/checkstyle.git")){
+ try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE)){
MethodTracker methodTracker = CodeTracker.methodTracker()
.repository(repository)
- .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
- .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
- .methodName("fireErrors")
- .methodDeclarationLineNumber(384)
+ .filePath("CryptoServer/src/main/java/com/cryptography/service/CryptoService.java")
+ .startCommitId("d0d9edcfb611ee35978938f7e35250f1ae982057")
+ .methodName("ceateUser")
+ .methodDeclarationLineNumber(43)
.build();
History methodHistory = methodTracker.track();
@@ -79,65 +51,125 @@ public static void main(String args[]) throws Exception {
}
System.out.println("======================================================");
}
- // VARIABLE TRACKING EXAMPLE
- try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
- "https://github.com/checkstyle/checkstyle.git")){
-
- VariableTracker variableTracker = CodeTracker.variableTracker()
- .repository(repository)
- .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
- .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
- .methodName("fireErrors")
- .methodDeclarationLineNumber(384)
- .variableName("stripped")
- .variableDeclarationLineNumber(385)
- .build();
-
- History variableHistory = variableTracker.track();
- for (History.HistoryInfo historyInfo : variableHistory.getHistoryInfoList()) {
- System.out.println("======================================================");
- System.out.println("Commit ID: " + historyInfo.getCommitId());
- System.out.println("Date: " +
- LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
- System.out.println("Before: " + historyInfo.getElementBefore().getName());
- System.out.println("After: " + historyInfo.getElementAfter().getName());
-
- for (Change change : historyInfo.getChangeList()) {
- System.out.println(change.getType().getTitle() + ": " + change);
- }
- }
- System.out.println("======================================================");
- }
-
- // ATTRIBUTE TRACKING EXAMPLE
- try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
- "https://github.com/checkstyle/checkstyle.git")) {
-
- AttributeTracker attributeTracker = CodeTracker.attributeTracker()
- .repository(repository)
- .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
- .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
- .attributeName("cacheFile")
- .attributeDeclarationLineNumber(132)
- .build();
-
- History attributeHistory = attributeTracker.track();
-
- for (History.HistoryInfo historyInfo : attributeHistory.getHistoryInfoList()) {
- System.out.println("======================================================");
- System.out.println("Commit ID: " + historyInfo.getCommitId());
- System.out.println("Date: " +
- LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
- System.out.println("Before: " + historyInfo.getElementBefore().getName());
- System.out.println("After: " + historyInfo.getElementAfter().getName());
-
- for (Change change : historyInfo.getChangeList()) {
- System.out.println(change.getType().getTitle() + ": " + change);
- }
- }
- System.out.println("======================================================");
- }
+// // BLOCK TRACKING EXAMPLE
+// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
+// "https://github.com/checkstyle/checkstyle.git")){
+//
+// BlockTracker blockTracker = CodeTracker.blockTracker()
+// .repository(repository)
+// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
+// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
+// .methodName("fireErrors")
+// .methodDeclarationLineNumber(384)
+// .codeElementType(CodeElementType.ENHANCED_FOR_STATEMENT)
+// .blockStartLineNumber(391)
+// .blockEndLineNumber(393)
+// .build();
+//
+// History blockHistory = blockTracker.track();
+//
+// for (History.HistoryInfo historyInfo : blockHistory.getHistoryInfoList()) {
+// System.out.println("======================================================");
+// System.out.println("Commit ID: " + historyInfo.getCommitId());
+// System.out.println("Date: " +
+// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+// System.out.println("Before: " + historyInfo.getElementBefore().getName());
+// System.out.println("After: " + historyInfo.getElementAfter().getName());
+//
+// for (Change change : historyInfo.getChangeList()) {
+// System.out.println(change.getType().getTitle() + ": " + change);
+// }
+// }
+// System.out.println("======================================================");
+// }
+// // METHOD TRACKING EXAMPLE
+// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
+// "https://github.com/checkstyle/checkstyle.git")){
+//
+// MethodTracker methodTracker = CodeTracker.methodTracker()
+// .repository(repository)
+// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
+// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
+// .methodName("fireErrors")
+// .methodDeclarationLineNumber(384)
+// .build();
+//
+// History methodHistory = methodTracker.track();
+//
+// for (History.HistoryInfo historyInfo : methodHistory.getHistoryInfoList()) {
+// System.out.println("======================================================");
+// System.out.println("Commit ID: " + historyInfo.getCommitId());
+// System.out.println("Date: " +
+// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+// System.out.println("Before: " + historyInfo.getElementBefore().getName());
+// System.out.println("After: " + historyInfo.getElementAfter().getName());
+//
+// for (Change change : historyInfo.getChangeList()) {
+// System.out.println(change.getType().getTitle() + ": " + change);
+// }
+// }
+// System.out.println("======================================================");
+// }
+// // VARIABLE TRACKING EXAMPLE
+// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
+// "https://github.com/checkstyle/checkstyle.git")){
+//
+// VariableTracker variableTracker = CodeTracker.variableTracker()
+// .repository(repository)
+// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
+// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
+// .methodName("fireErrors")
+// .methodDeclarationLineNumber(384)
+// .variableName("stripped")
+// .variableDeclarationLineNumber(385)
+// .build();
+//
+// History variableHistory = variableTracker.track();
+//
+// for (History.HistoryInfo historyInfo : variableHistory.getHistoryInfoList()) {
+// System.out.println("======================================================");
+// System.out.println("Commit ID: " + historyInfo.getCommitId());
+// System.out.println("Date: " +
+// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+// System.out.println("Before: " + historyInfo.getElementBefore().getName());
+// System.out.println("After: " + historyInfo.getElementAfter().getName());
+//
+// for (Change change : historyInfo.getChangeList()) {
+// System.out.println(change.getType().getTitle() + ": " + change);
+// }
+// }
+// System.out.println("======================================================");
+// }
+//
+// // ATTRIBUTE TRACKING EXAMPLE
+// try (Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE + "checkstyle\\checkstyle",
+// "https://github.com/checkstyle/checkstyle.git")) {
+//
+// AttributeTracker attributeTracker = CodeTracker.attributeTracker()
+// .repository(repository)
+// .filePath("src/main/java/com/puppycrawl/tools/checkstyle/Checker.java")
+// .startCommitId("119fd4fb33bef9f5c66fc950396669af842c21a3")
+// .attributeName("cacheFile")
+// .attributeDeclarationLineNumber(132)
+// .build();
+//
+// History attributeHistory = attributeTracker.track();
+//
+// for (History.HistoryInfo historyInfo : attributeHistory.getHistoryInfoList()) {
+// System.out.println("======================================================");
+// System.out.println("Commit ID: " + historyInfo.getCommitId());
+// System.out.println("Date: " +
+// LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+// System.out.println("Before: " + historyInfo.getElementBefore().getName());
+// System.out.println("After: " + historyInfo.getElementAfter().getName());
+//
+// for (Change change : historyInfo.getChangeList()) {
+// System.out.println(change.getType().getTitle() + ": " + change);
+// }
+// }
+// System.out.println("======================================================");
+// }
}
}
diff --git a/src/main/java/org/codetracker/MethodTrackerChangeHistory.java b/src/main/java/org/codetracker/MethodTrackerChangeHistory.java
index c87bb5ea8dd..5aa7e2f29da 100644
--- a/src/main/java/org/codetracker/MethodTrackerChangeHistory.java
+++ b/src/main/java/org/codetracker/MethodTrackerChangeHistory.java
@@ -448,7 +448,7 @@ else if (looserMapper(mapperWithLessMappings, mapper)) {
}
break;
}
- case EXTRACT_FIXTURE:
+// case EXTRACT_FIXTURE: TODO: upgrade RM version then uncomment
case MOVE_CODE: {
MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring;
operationBefore = moveCodeRefactoring.getSourceContainer();
diff --git a/src/main/java/org/codetracker/VariableTrackerChangeHistory.java b/src/main/java/org/codetracker/VariableTrackerChangeHistory.java
index 848c6e7465f..4a0cbb52840 100644
--- a/src/main/java/org/codetracker/VariableTrackerChangeHistory.java
+++ b/src/main/java/org/codetracker/VariableTrackerChangeHistory.java
@@ -238,7 +238,7 @@ public boolean checkForExtractionOrInline(Version currentVersion, Version parent
}
break;
}
- case EXTRACT_FIXTURE:
+// case EXTRACT_FIXTURE: TODO: upgrade RM version then uncomment
case MOVE_CODE: {
MoveCodeRefactoring moveCodeRefactoring = (MoveCodeRefactoring) refactoring;
Method extractedMethod = Method.of(moveCodeRefactoring.getTargetContainer(), currentVersion);
diff --git a/src/test/java/org/codetracker/AttributeTrackerImplTest.java b/src/test/java/org/codetracker/AttributeTrackerImplTest.java
new file mode 100644
index 00000000000..aab4144716d
--- /dev/null
+++ b/src/test/java/org/codetracker/AttributeTrackerImplTest.java
@@ -0,0 +1,58 @@
+package org.codetracker;
+
+import org.codetracker.api.AttributeTracker;
+import org.codetracker.api.CodeTracker;
+import org.codetracker.api.History;
+import org.codetracker.change.Change;
+import org.codetracker.element.Attribute;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.jupiter.api.Test;
+import org.refactoringminer.api.GitService;
+import org.refactoringminer.util.GitServiceImpl;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AttributeTrackerImplTest {
+ String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting";
+ String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git";
+
+ @Test
+ public void returnFalseRename() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+
+ AttributeTracker attributeTracker = CodeTracker.attributeTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/genericcase/ServiceInfo.java")
+ .startCommitId("88cbdb012a2e92d029f20b5253272f32ce84c3be")
+ .attributeName("serviceName")
+ .attributeDeclarationLineNumber(3)
+ .build();
+ History attributeHistory = attributeTracker.track();
+ printHappyCaseOutput(attributeHistory);
+ assertNotNull(attributeHistory);
+ assertTrue(!attributeHistory.getHistoryInfoList().isEmpty() && attributeHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+ private void printHappyCaseOutput(History attributeHistory){
+ for (History.HistoryInfo historyInfo : attributeHistory.getHistoryInfoList()) {
+ System.out.println("======================================================");
+ System.out.println("Commit ID: " + historyInfo.getCommitId());
+ System.out.println("Date: " +
+ LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+ System.out.println("Before: " + historyInfo.getElementBefore().getName());
+ System.out.println("After: " + historyInfo.getElementAfter().getName());
+
+ for (Change change : historyInfo.getChangeList()) {
+ System.out.println(change.getType().getTitle() + ": " + change);
+ }
+ }
+ System.out.println("======================================================");
+ }
+
+ }
diff --git a/src/test/java/org/codetracker/BlameTest.java b/src/test/java/org/codetracker/BlameTest.java
new file mode 100644
index 00000000000..b32b60e109c
--- /dev/null
+++ b/src/test/java/org/codetracker/BlameTest.java
@@ -0,0 +1,122 @@
+package org.codetracker;
+
+import gr.uom.java.xmi.LocationInfo;
+import org.codetracker.api.BlockTracker;
+import org.codetracker.api.CodeTracker;
+import org.codetracker.api.History;
+import org.codetracker.api.MethodTracker;
+import org.codetracker.change.Change;
+import org.codetracker.element.Block;
+import org.codetracker.element.Method;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.jupiter.api.Test;
+import org.refactoringminer.api.GitService;
+import org.refactoringminer.util.GitServiceImpl;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BlameTest {
+ String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting";
+ String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git";
+
+ /***************************************************************************
+ * *************************************
+ * Blame
+ * False Rename Detection Instead Of Extract Class & Move Method
+ * *************************************
+ */
+ @Test
+ public void returnBlameWhenExtractClassNew() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/newclass/Calculator.java")
+ .startCommitId("8ef06dced725cca8cc22705d4eba95ef39ae1e36")
+ .methodName("print")
+ .methodDeclarationLineNumber(17)
+ .build();
+ History.HistoryInfo methodHistory = methodTracker.blame();
+ printHappyCaseOutputMethod(methodHistory);
+ assertNotNull(methodHistory);
+// assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+ }
+
+ @Test
+ public void returnsBlameRenameMethodGivenCommitInfoOfRepository() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/service/CryptoService.java")
+ .startCommitId("d0d9edcfb611ee35978938f7e35250f1ae982057")
+ .methodName("ceateUser")
+ .methodDeclarationLineNumber(46) //54 43
+ .build();
+ History.HistoryInfo methodHistory = methodTracker.blame();
+ printHappyCaseOutputMethod(methodHistory);
+ assertNotNull(methodHistory);
+// assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().size() > 2);
+ }
+
+ @Test
+ public void ifToTernaryOperatorTest() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ BlockTracker blockTracker = CodeTracker.blockTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/ternary/Main.java")
+ .startCommitId("3b7a3dfdbc8c9a6233a7d631bc9ff1eb35bc0653")
+ .methodName("isPassed")
+ .methodDeclarationLineNumber(5)
+ .codeElementType(LocationInfo.CodeElementType.RETURN_STATEMENT)
+ .blockStartLineNumber(6)
+ .blockEndLineNumber(6)
+ .blameLineNumber(6)
+ .build();
+
+ History.HistoryInfo blockHistory = blockTracker.blame();
+ printHappyCaseOutputBlock(blockHistory);
+ assertNotNull(blockHistory);
+// assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+
+ private void printHappyCaseOutputBlock(History.HistoryInfo blockHistory) {
+ System.out.println("======================================================");
+ System.out.println("Commit ID: " + blockHistory.getCommitId());
+ System.out.println("Date: " +
+ LocalDateTime.ofEpochSecond(blockHistory.getCommitTime(), 0, ZoneOffset.UTC));
+ System.out.println("CommitterName: " + blockHistory.getCommitterName());
+ System.out.println("Before: " + blockHistory.getElementBefore().getName());
+ System.out.println("After: " + blockHistory.getElementAfter().getName());
+
+ for (Change change : blockHistory.getChangeList()) {
+ System.out.println(change.getType().getTitle() + ": " + change);
+ }
+ System.out.println("======================================================");
+ }
+
+ private void printHappyCaseOutputMethod(History.HistoryInfo methodHistory) {
+ System.out.println("======================================================");
+ System.out.println("Commit ID: " + methodHistory.getCommitId());
+ System.out.println("Date: " +
+ LocalDateTime.ofEpochSecond(methodHistory.getCommitTime(), 0, ZoneOffset.UTC));
+ System.out.println("CommitterName: " + methodHistory.getCommitterName());
+ System.out.println("Before: " + methodHistory.getElementBefore().getName());
+ System.out.println("After: " + methodHistory.getElementAfter().getName());
+
+ for (Change change : methodHistory.getChangeList()) {
+ System.out.println(change.getType().getTitle() + ": " + change);
+ }
+ System.out.println("======================================================");
+ }
+
+}
diff --git a/src/test/java/org/codetracker/BlockTrackerImplTest.java b/src/test/java/org/codetracker/BlockTrackerImplTest.java
new file mode 100644
index 00000000000..98fe3746d20
--- /dev/null
+++ b/src/test/java/org/codetracker/BlockTrackerImplTest.java
@@ -0,0 +1,118 @@
+package org.codetracker;
+
+import gr.uom.java.xmi.LocationInfo;
+import org.codetracker.api.BlockTracker;
+import org.codetracker.api.CodeTracker;
+import org.codetracker.api.History;
+import org.codetracker.change.Change;
+import org.codetracker.element.Block;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.jupiter.api.Test;
+import org.refactoringminer.api.GitService;
+import org.refactoringminer.util.GitServiceImpl;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BlockTrackerImplTest {
+ String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting";
+ String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git";
+ @Test
+ public void ifToTernaryOperatorTest() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ BlockTracker blockTracker = CodeTracker.blockTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/ternary/Main.java")
+ .startCommitId("3b7a3dfdbc8c9a6233a7d631bc9ff1eb35bc0653")
+ .methodName("isPassed")
+ .methodDeclarationLineNumber(5)
+ .codeElementType(LocationInfo.CodeElementType.RETURN_STATEMENT)
+ .blockStartLineNumber(6)
+ .blockEndLineNumber(6)
+ .build();
+
+ History blockHistory = blockTracker.track();
+ printHappyCaseOutput(blockHistory);
+ assertNotNull(blockHistory);
+ assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+ @Test
+ public void testDelegateMethodLocalFile() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ BlockTracker blockTracker = CodeTracker.blockTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/delegate/Main.java")
+ .startCommitId("97eb547f6fae7c09aba90f030f0557a77ee86cdf")
+ .methodName("getTheYoungestEmployeeSalary")
+ .methodDeclarationLineNumber(12)
+ .codeElementType(LocationInfo.CodeElementType.ENHANCED_FOR_STATEMENT)
+ .blockStartLineNumber(15)
+ .blockEndLineNumber(17)
+ .build();
+
+ History blockHistory = blockTracker.track();
+ printHappyCaseOutput(blockHistory);
+ assertNotNull(blockHistory);
+ assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+ }
+ @Test
+ public void testDelegateSameSignatureMethodOtherFile() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ BlockTracker blockTracker = CodeTracker.blockTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/blocktracking/delegate/Utilitu.java")
+ .startCommitId("f99f42b46810963f9e27973a28169734c5ee2ce3")
+ .methodName("getTheYoungestEmployeeSalary")
+ .methodDeclarationLineNumber(6)
+ .codeElementType(LocationInfo.CodeElementType.ENHANCED_FOR_STATEMENT)
+ .blockStartLineNumber(8)
+ .blockEndLineNumber(10)
+ .build();
+
+ History blockHistory = blockTracker.track();
+ printHappyCaseOutput(blockHistory);
+ assertNotNull(blockHistory);
+ assertTrue(!blockHistory.getHistoryInfoList().isEmpty() && blockHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+ }
+ private void printHappyCaseOutput(History blockHistory){
+ for (History.HistoryInfo historyInfo : blockHistory.getHistoryInfoList()) {
+ System.out.println("======================================================");
+ System.out.println("Commit ID: " + historyInfo.getCommitId());
+ System.out.println("Date: " +
+ LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+ System.out.println("Before: " + historyInfo.getElementBefore().getName());
+ System.out.println("After: " + historyInfo.getElementAfter().getName());
+
+ for (Change change : historyInfo.getChangeList()) {
+ System.out.println(change.getType().getTitle() + ": " + change);
+ }
+ }
+ System.out.println("======================================================");
+ }
+
+}
+// System.out.println("---- Statements visible to CodeTracker ----");
+// for (AbstractStatement st :
+// startMethod.getUmlOperation()
+// .getBody()
+// .getCompositeStatement()
+// .getAllStatements()) {
+//
+// System.out.printf("%s %d-%d%n",
+// st.getLocationInfo().getCodeElementType(),
+// st.getLocationInfo().getStartLine(),
+// st.getLocationInfo().getEndLine());
+// }
+// System.out.println("-------------------------------------------");
+
+
diff --git a/src/test/java/org/codetracker/FalseRenameDetectionTests.java b/src/test/java/org/codetracker/FalseRenameDetectionTests.java
new file mode 100644
index 00000000000..3aa64101f68
--- /dev/null
+++ b/src/test/java/org/codetracker/FalseRenameDetectionTests.java
@@ -0,0 +1,170 @@
+package org.codetracker;
+
+import org.codetracker.api.CodeTracker;
+import org.codetracker.api.History;
+import org.codetracker.api.MethodTracker;
+import org.codetracker.change.Change;
+import org.codetracker.element.Method;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.jupiter.api.Test;
+import org.refactoringminer.api.GitService;
+import org.refactoringminer.util.GitServiceImpl;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FalseRenameDetectionTests {
+ String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting";
+ String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git";
+ /***************************************************************************
+ * *************************************
+ * False Rename Detection Instead Of Extract Class & Move Method
+ * *************************************
+ */
+ @Test
+ public void breakRMReturnRenameWhenExtractClassNew() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/newclass/Calculator.java")
+ .startCommitId("8ef06dced725cca8cc22705d4eba95ef39ae1e36")
+ .methodName("print")
+ .methodDeclarationLineNumber(15)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+ }
+ @Test
+ public void breakRMReturnRenameWhenExtractClassExtend() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/extend/Calculator.java")
+ .startCommitId("a14ddd9082a29b53a0fe4b153f65b777fe122931")
+ .methodName("print")
+ .methodDeclarationLineNumber(20)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+ }
+ @Test
+ public void breakRMReturnRenameWhenExtractClassFieldInjection() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/autowired/Calculator.java")
+ .startCommitId("0eae1091dc5ce1eddbd1edd62dcb12936ebb1978")
+ .methodName("print")
+ .methodDeclarationLineNumber(18)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+ }
+
+
+ @Test
+ public void breakRMReturnRenameWhenExtractClassConstructionInjection() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/constructor/Calculator.java")
+ .startCommitId("ebe0ba262dc9c92473336baeb4d1d71df77f7d92")
+ .methodName("print")
+ .methodDeclarationLineNumber(19)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+
+ @Test
+ public void breakRMReturnRenameWhenExtractClassSetterInjection() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/setter/Calculator.java")
+ .startCommitId("ea57744b29d0a8edf24348cf85db997f9192c12c")
+ .methodName("print")
+ .methodDeclarationLineNumber(20)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+ @Test
+ public void returnRenameWhenExtractClassStaticMethod() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/staticmethod/DiscountUtil.java")
+ .startCommitId("33bf82563170a549f8366271055a43ee1a53e13d")
+ .methodName("calculateDiscount")
+ .methodDeclarationLineNumber(4)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+ @Test
+ public void returnRenameWhenExtractClassFunctionalInterfaceSupplier() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/dependencyinjection/functionalinterface/Calculator.java")
+ .startCommitId("2df483b1cd01681f1cdc976ddc53d010b8e9cf23")
+ .methodName("sum")
+ .methodDeclarationLineNumber(7)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+
+ private void printHappyCaseOutput(History methodHistory){
+ for (History.HistoryInfo historyInfo : methodHistory.getHistoryInfoList()) {
+ System.out.println("======================================================");
+ System.out.println("Commit ID: " + historyInfo.getCommitId());
+ System.out.println("Date: " +
+ LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+ System.out.println("Before: " + historyInfo.getElementBefore().getName());
+ System.out.println("After: " + historyInfo.getElementAfter().getName());
+
+ for (Change change : historyInfo.getChangeList()) {
+ System.out.println(change.getType().getTitle() + ": " + change);
+ }
+ }
+ System.out.println("======================================================");
+ }
+
+}
diff --git a/src/test/java/org/codetracker/MethodTrackerImplTest.java b/src/test/java/org/codetracker/MethodTrackerImplTest.java
new file mode 100644
index 00000000000..659e7872a77
--- /dev/null
+++ b/src/test/java/org/codetracker/MethodTrackerImplTest.java
@@ -0,0 +1,218 @@
+package org.codetracker;
+
+
+import org.codetracker.api.CodeTracker;
+import org.codetracker.api.History;
+import org.codetracker.api.MethodTracker;
+import org.codetracker.change.Change;
+import org.codetracker.element.Method;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.jupiter.api.Test;
+import org.refactoringminer.api.GitService;
+import org.refactoringminer.util.GitServiceImpl;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class MethodTrackerImplTest {
+
+ String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting";
+ String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git";
+
+ @Test
+ public void returnsHistoryOfRenameMethodGivenCommitInfoOfRepository() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/service/CryptoService.java")
+ .startCommitId("d0d9edcfb611ee35978938f7e35250f1ae982057")
+ .methodName("ceateUser")
+ .methodDeclarationLineNumber(43)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().size() > 2);
+ }
+ @Test
+ public void returnsHistoryOfMovedMethodContainerChanged() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/CryptographyApplication.java")
+ .startCommitId("8e0ada4854818a764d337d40c25f6060aa8ea1b2")
+ .methodName("main")
+ .methodDeclarationLineNumber(42)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change"));
+ }
+ @Test
+ public void returnNoChangeForBodyChangeDueToCollision() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/CryptographyApplication.java")
+ .startCommitId("5c08104df4a48e253714226c0d9c0d0f4e40ee30")
+ .methodName("run")
+ .methodDeclarationLineNumber(47)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("body change"));
+ }
+ @Test
+ public void returnContainerChangeForMovedFile() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/dto/request/DecryptReqDto.java")
+ .startCommitId("213ed6bbd3c0b94423e7338b191e26f876c0c0d5")
+ .methodName("getBase64OfCipherTest")
+ .methodDeclarationLineNumber(17)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change"));
+
+ }
+@Test
+public void returnTheLastRefactoringTypeBeforeRenamingFile() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/dto/EncryptReqDto.java")
+ .startCommitId("213ed6bbd3c0b94423e7338b191e26f876c0c0d5")
+ .methodName("getPlainText")
+ .methodDeclarationLineNumber(6)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change"));
+
+}
+ @Test
+ public void returnContainerChangeAfterRenamingFile() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/dto/EncryptRequestDto.java")
+ .startCommitId("f8b244f015b79c95dfb7fb86340c749447f61b34")
+ .methodName("getPlainText")
+ .methodDeclarationLineNumber(6)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change"));
+
+ }
+ @Test
+ public void testMoveFileAndBodyChangeSimultaneously() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/dto/Response/EncryptResDto.java")
+ .startCommitId("d64f222f3512c8ce5453d53c77093ae4b2a995ca")
+ .methodName("getCipherText")
+ .methodDeclarationLineNumber(6)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change"));
+ }
+ @Test
+ public void testConstructorWithMinimalChangeCommit() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/dto/UserDto.java")
+ .startCommitId("d64f222f3512c8ce5453d53c77093ae4b2a995ca")
+ .methodName("UserDto")
+ .methodDeclarationLineNumber(9)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change"));
+
+ }
+ @Test
+ public void testMethodClassImplementsInterface() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/config/CustomUserDetailManagerImpl.java")
+ .startCommitId("d64f222f3512c8ce5453d53c77093ae4b2a995ca")
+ .methodName("createUser")
+ .methodDeclarationLineNumber(22)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(1).getChangeType().getTitle().equals("container change"));
+ }
+ @Test
+ public void uncommentedMethod() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+ MethodTracker methodTracker = CodeTracker.methodTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/comment_to_method/Main.java")
+ .startCommitId("f1860ea8ac8f61b539b83236e6b7ca020e069cdb")
+ .methodName("goodbye")
+ .methodDeclarationLineNumber(7)
+ .build();
+ History methodHistory = methodTracker.track();
+ printHappyCaseOutput(methodHistory);
+ assertNotNull(methodHistory);
+ assertTrue(!methodHistory.getHistoryInfoList().isEmpty() && methodHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+ private void printHappyCaseOutput(History methodHistory){
+ for (History.HistoryInfo historyInfo : methodHistory.getHistoryInfoList()) {
+ System.out.println("======================================================");
+ System.out.println("Commit ID: " + historyInfo.getCommitId());
+ System.out.println("Date: " +
+ LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+ System.out.println("CommitterName: " + historyInfo.getCommitterName());
+
+ System.out.println("Before: " + historyInfo.getElementBefore().getName());
+ System.out.println("After: " + historyInfo.getElementAfter().getName());
+
+ for (Change change : historyInfo.getChangeList()) {
+ System.out.println(change.getType().getTitle() + ": " + change);
+ }
+ }
+ System.out.println("======================================================");
+ }
+
+}
diff --git a/src/test/java/org/codetracker/VariableTrackerImplTest.java b/src/test/java/org/codetracker/VariableTrackerImplTest.java
new file mode 100644
index 00000000000..3628d2bba85
--- /dev/null
+++ b/src/test/java/org/codetracker/VariableTrackerImplTest.java
@@ -0,0 +1,101 @@
+package org.codetracker;
+
+import org.codetracker.api.CodeTracker;
+import org.codetracker.api.History;
+import org.codetracker.api.VariableTracker;
+import org.codetracker.change.Change;
+import org.codetracker.element.Variable;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.jupiter.api.Test;
+import org.refactoringminer.api.GitService;
+import org.refactoringminer.util.GitServiceImpl;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class VariableTrackerImplTest {
+ String FOLDER_TO_CLONE = "D:\\work\\programming\\CodeTrackerTesting";
+ String GITHUB_REPO_TO_CLONE = "https://github.com/parinaz-st/CryptographyRestService.git";
+ @Test
+ public void testSimpleVariableTracker() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+
+ VariableTracker variableTracker = CodeTracker.variableTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/variabletracking/Main.java")
+ .startCommitId("6ad3db2a40834eba62b82c36aa07ab7e01c469d7")
+ .methodName("calculate")
+ .methodDeclarationLineNumber(5)
+ .variableName("num")
+ .variableDeclarationLineNumber(6)
+ .build();
+ History variableHistory = variableTracker.track();
+ printHappyCaseOutput(variableHistory);
+ assertNotNull(variableHistory);
+ assertTrue(!variableHistory.getHistoryInfoList().isEmpty() && variableHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+ }
+ @Test
+ public void returnMultiVariableDeclarationRefactoring() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+
+ VariableTracker variableTracker = CodeTracker.variableTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/multiVariableDeclaration/Main.java")
+ .startCommitId("5bcc51064d77c323f4422009e66e2c6de93c86f1")
+ .methodName("main")
+ .methodDeclarationLineNumber(6)
+ .variableName("n")
+ .variableDeclarationLineNumber(8)
+ .build();
+ History variableHistory = variableTracker.track();
+ printHappyCaseOutput(variableHistory);
+ assertNotNull(variableHistory);
+ assertTrue(!variableHistory.getHistoryInfoList().isEmpty() && variableHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+ @Test
+ public void returnMultiVariableDeclarationSlideStatementRefactoring() throws Exception {
+ GitService gitService = new GitServiceImpl();
+ Repository repository = gitService.cloneIfNotExists(FOLDER_TO_CLONE,
+ GITHUB_REPO_TO_CLONE);
+
+ VariableTracker variableTracker = CodeTracker.variableTracker()
+ .repository(repository)
+ .filePath("CryptoServer/src/main/java/com/cryptography/codetrackertest/multiVariableDeclaration/SlideStatement.java")
+ .startCommitId("b7a6cdde69b3af5247a1289ce34a3b5e5cff4593")
+ .methodName("main")
+ .methodDeclarationLineNumber(6)
+ .variableName("n")
+ .variableDeclarationLineNumber(9)
+ .build();
+ History variableHistory = variableTracker.track();
+ printHappyCaseOutput(variableHistory);
+ assertNotNull(variableHistory);
+ assertTrue(!variableHistory.getHistoryInfoList().isEmpty() && variableHistory.getHistoryInfoList().get(0).getChangeType().getTitle().equals("introduced"));
+
+ }
+ public void printHappyCaseOutput(History variableHistory){
+ for (History.HistoryInfo historyInfo : variableHistory.getHistoryInfoList()) {
+ System.out.println("======================================================");
+ System.out.println("Commit ID: " + historyInfo.getCommitId());
+ System.out.println("Date: " +
+ LocalDateTime.ofEpochSecond(historyInfo.getCommitTime(), 0, ZoneOffset.UTC));
+ System.out.println("Before: " + historyInfo.getElementBefore().getName());
+ System.out.println("After: " + historyInfo.getElementAfter().getName());
+
+ for (Change change : historyInfo.getChangeList()) {
+ System.out.println(change.getType().getTitle() + ": " + change);
+ }
+ }
+ System.out.println("======================================================");
+
+ }
+
+}
From 0c9eb5ff17d3bab7941e37c1bf5b10ede446dc20 Mon Sep 17 00:00:00 2001
From: parinaz-st
Date: Thu, 28 May 2026 19:20:49 +0330
Subject: [PATCH 2/5] Added support for uncommented Block detection, Comment
statement transition detection for matched blocks
---
.../BlockTrackerChangeHistory.java | 80 ++++---
.../java/org/codetracker/change/Change.java | 6 +-
.../org/codetracker/change/ChangeFactory.java | 13 ++
.../change/block/CommentedOutCodeInBlock.java | 13 ++
.../change/block/UncommentedBlock.java | 13 ++
.../change/block/UncommentedCodeInBlock.java | 15 ++
.../util/CommentTransitionAnalyzer.java | 212 ++++++++++++++++++
7 files changed, 317 insertions(+), 35 deletions(-)
create mode 100644 src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java
create mode 100644 src/main/java/org/codetracker/change/block/UncommentedBlock.java
create mode 100644 src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java
create mode 100644 src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
diff --git a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
index bf01484a079..d3f9e955cc1 100644
--- a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
+++ b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
@@ -2,17 +2,10 @@
import java.io.IOException;
import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.function.Predicate;
-
+import gr.uom.java.xmi.*;
+import gr.uom.java.xmi.diff.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.codetracker.api.History;
@@ -37,6 +30,7 @@
import org.codetracker.change.method.BodyChange;
import org.codetracker.element.Block;
import org.codetracker.element.Method;
+import org.codetracker.util.Util;
import org.refactoringminer.api.Refactoring;
import com.github.difflib.DiffUtils;
@@ -46,11 +40,7 @@
import com.github.difflib.patch.InsertDelta;
import com.github.difflib.patch.Patch;
-import gr.uom.java.xmi.UMLOperation;
-import gr.uom.java.xmi.UMLType;
-import gr.uom.java.xmi.VariableDeclarationContainer;
import gr.uom.java.xmi.LocationInfo.CodeElementType;
-import gr.uom.java.xmi.UMLAnonymousClass;
import gr.uom.java.xmi.decomposition.AbstractCodeFragment;
import gr.uom.java.xmi.decomposition.AbstractCodeMapping;
import gr.uom.java.xmi.decomposition.AbstractExpression;
@@ -63,23 +53,9 @@
import gr.uom.java.xmi.decomposition.TryStatementObject;
import gr.uom.java.xmi.decomposition.UMLOperationBodyMapper;
import gr.uom.java.xmi.decomposition.VariableDeclaration;
-import gr.uom.java.xmi.diff.ExtractOperationRefactoring;
-import gr.uom.java.xmi.diff.InlineOperationRefactoring;
-import gr.uom.java.xmi.diff.MergeOperationRefactoring;
-import gr.uom.java.xmi.diff.MoveCodeRefactoring;
-import gr.uom.java.xmi.diff.MoveOperationRefactoring;
-import gr.uom.java.xmi.diff.PullUpOperationRefactoring;
-import gr.uom.java.xmi.diff.PushDownOperationRefactoring;
-import gr.uom.java.xmi.diff.RenameOperationRefactoring;
-import gr.uom.java.xmi.diff.ReplaceAnonymousWithClassRefactoring;
-import gr.uom.java.xmi.diff.ReplaceAnonymousWithLambdaRefactoring;
-import gr.uom.java.xmi.diff.ReplaceConditionalWithTernaryRefactoring;
-import gr.uom.java.xmi.diff.ReplaceLoopWithPipelineRefactoring;
-import gr.uom.java.xmi.diff.ReplacePipelineWithLoopRefactoring;
-import gr.uom.java.xmi.diff.SplitConditionalRefactoring;
-import gr.uom.java.xmi.diff.SplitOperationRefactoring;
-import gr.uom.java.xmi.diff.UMLAbstractClassDiff;
-import gr.uom.java.xmi.diff.UMLAnonymousClassDiff;
+
+import static org.codetracker.util.CommentTransitionAnalyzer.*;
+
public class BlockTrackerChangeHistory extends AbstractChangeHistory {
private final ChangeHistory blockChangeHistory = new ChangeHistory<>();
@@ -693,9 +669,38 @@ public boolean checkBodyOfMatchedOperations(Version currentVersion, Version pare
// check if it is in the matched
if (isMatched(umlOperationBodyMapper, currentVersion, parentVersion, equalOperator))
return true;
- //Check if is added
+ //check if block got uncommented
+ if(isBlockUncommented(umlOperationBodyMapper, currentVersion, parentVersion, equalOperator)){
+ return true;
+ }
+// Check if is added
return isAdded(umlOperationBodyMapper, currentVersion, parentVersion, equalOperator);
}
+ public boolean isBlockUncommented(UMLOperationBodyMapper umlOperationBodyMapper, Version currentVersion, Version parentVersion, Predicate equalOperator) {
+ List currentNonMappedInnerNodes = umlOperationBodyMapper.getNonMappedInnerNodesT2();
+ UMLCommentListDiff umlCommentListDiff = umlOperationBodyMapper.getCommentListDiff();
+ List deletedComments = umlCommentListDiff.getDeletedComments();
+ if(currentNonMappedInnerNodes.size() == 0 || deletedComments == null || deletedComments.isEmpty())
+ return false;
+ Map deletedCommentsHashMap = generateCommentTextHashMap(deletedComments);
+ for(CompositeStatementObject nonMappedInnerNode : currentNonMappedInnerNodes) {
+ String hashOfNonMappedInnerNode = Util.getSHA512(nonMappedInnerNode.getActualSignature());
+ if (!deletedCommentsHashMap.containsKey(hashOfNonMappedInnerNode))
+ continue;
+ Block blockAfter = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), currentVersion);
+ if (!equalOperator.test(blockAfter))
+ continue;
+ //check block body continuity
+ if (!isBlockBodyUnchangedDuringUncomment(umlOperationBodyMapper, nonMappedInnerNode, deletedCommentsHashMap, blockAfter))
+ continue;
+ Block virtualBlockBefore = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), parentVersion); // add metadata later, flag isVirtual = true;
+ blockChangeHistory.addChange(virtualBlockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.UNCOMMENTED_BLOCK));
+ elements.add(virtualBlockBefore);
+ blockChangeHistory.connectRelatedNodes();
+ return true;
+ }
+ return false;
+ }
public boolean isBlockRefactored(Collection refactorings, Version currentVersion, Version parentVersion, Predicate equalOperator) {
Set leftBlockSet = analyseBlockRefactorings(refactorings, currentVersion, parentVersion, equalOperator);
@@ -1137,7 +1142,16 @@ public boolean isMatched(UMLOperationBodyMapper umlOperationBodyMapper, Version
List stringRepresentationBodyBefore = stringRepresentationBefore.subList(1, stringRepresentationBefore.size());
List stringRepresentationBodyAfter = stringRepresentationAfter.subList(1, stringRepresentationAfter.size());
if (!stringRepresentationBodyBefore.equals(stringRepresentationBodyAfter)) {
- blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
+ Set commentTransitions = DetectCommentTransitionsInsideMatchedBlock(umlOperationBodyMapper, blockBefore, blockAfter, currentVersion, parentVersion);
+ if(!commentTransitions.isEmpty()){
+ for(Change.Type changeType : commentTransitions){
+ blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(changeType));
+ }
+ }
+ else{
+ blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
+ }
+
}
bodyChange = true;
}
diff --git a/src/main/java/org/codetracker/change/Change.java b/src/main/java/org/codetracker/change/Change.java
index 0eca191d9b1..e3ffd031dd2 100644
--- a/src/main/java/org/codetracker/change/Change.java
+++ b/src/main/java/org/codetracker/change/Change.java
@@ -56,8 +56,10 @@ enum Type {
INITIALIZER_ADDED("initializer added"),
INITIALIZER_REMOVED("initializer removed"),
SUPERCLASS_CHANGE("superclass change"),
- INTERFACE_LIST_CHANGE("interface list change");
-
+ INTERFACE_LIST_CHANGE("interface list change"),
+ COMMENTED_OUT_STATEMENT("commented out statement"),
+ UNCOMMENTED_STATEMENT("uncommented statement"),
+ UNCOMMENTED_BLOCK("uncommented Block");
private static final Map lookup = new HashMap<>();
static {
diff --git a/src/main/java/org/codetracker/change/ChangeFactory.java b/src/main/java/org/codetracker/change/ChangeFactory.java
index 405b85aa30d..78cdf3ad153 100644
--- a/src/main/java/org/codetracker/change/ChangeFactory.java
+++ b/src/main/java/org/codetracker/change/ChangeFactory.java
@@ -141,6 +141,19 @@ public AbstractChange build() {
change = new BodyChange();
break;
}
+ case UNCOMMENTED_STATEMENT:{
+ change = new UncommentedCodeInBlock();
+ break;
+ }
+ case COMMENTED_OUT_STATEMENT:{
+ change = new CommentedOutCodeInBlock();
+ break;
+ }
+ case UNCOMMENTED_BLOCK:{
+ change = new UncommentedBlock();
+ break;
+ }
+
case CATCH_BLOCK_CHANGE: {
change = new CatchBlockChange();
break;
diff --git a/src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java b/src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java
new file mode 100644
index 00000000000..f7469135110
--- /dev/null
+++ b/src/main/java/org/codetracker/change/block/CommentedOutCodeInBlock.java
@@ -0,0 +1,13 @@
+package org.codetracker.change.block;
+
+public class CommentedOutCodeInBlock extends BlockBodyChange {
+ public CommentedOutCodeInBlock() {
+ super(Type.COMMENTED_OUT_STATEMENT);
+ }
+
+ @Override
+ public String toString() {
+ return "Body Change, Contains Commented-out Code";
+ }
+
+}
diff --git a/src/main/java/org/codetracker/change/block/UncommentedBlock.java b/src/main/java/org/codetracker/change/block/UncommentedBlock.java
new file mode 100644
index 00000000000..df8c71c3620
--- /dev/null
+++ b/src/main/java/org/codetracker/change/block/UncommentedBlock.java
@@ -0,0 +1,13 @@
+package org.codetracker.change.block;
+
+public class UncommentedBlock extends BlockChange {
+ public UncommentedBlock() {
+ super(Type.UNCOMMENTED_BLOCK);
+ }
+
+ @Override
+ public String toString() {
+ return "Block Change, Uncommented Block";
+ }
+
+}
diff --git a/src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java b/src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java
new file mode 100644
index 00000000000..9f940f99931
--- /dev/null
+++ b/src/main/java/org/codetracker/change/block/UncommentedCodeInBlock.java
@@ -0,0 +1,15 @@
+package org.codetracker.change.block;
+
+import org.codetracker.change.Change;
+
+public class UncommentedCodeInBlock extends BlockBodyChange {
+ public UncommentedCodeInBlock() {
+ super(Change.Type.UNCOMMENTED_STATEMENT);
+ }
+
+ @Override
+ public String toString() {
+ return "Body Change, Contains Uncommented Code";
+ }
+
+}
diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
new file mode 100644
index 00000000000..311466a9f48
--- /dev/null
+++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
@@ -0,0 +1,212 @@
+package org.codetracker.util;
+
+import gr.uom.java.xmi.LocationInfo;
+import gr.uom.java.xmi.UMLComment;
+import gr.uom.java.xmi.decomposition.*;
+import org.codetracker.api.Version;
+import org.codetracker.change.Change;
+import org.codetracker.element.Block;
+
+import java.util.*;
+
+public class CommentTransitionAnalyzer {
+
+ public static Set DetectCommentTransitionsInsideMatchedBlock(UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore, Block blockAfter, Version currentVersion, Version parentVersion) {
+ Set changeTypes = new HashSet<>();
+ List deletedComments = umlOperationBodyMapper.getCommentListDiff().getDeletedComments();
+ List addedComments = umlOperationBodyMapper.getCommentListDiff().getAddedComments();
+ Map deletedCommentsHash;
+ Map addedCommentsHash;
+
+ if (!deletedComments.isEmpty()) {
+ deletedCommentsHash = generateCommentTextHashMap(deletedComments);
+ if (isUncommentedCodeDetected(deletedCommentsHash, umlOperationBodyMapper, blockAfter))
+ changeTypes.add(Change.Type.UNCOMMENTED_STATEMENT);
+ }
+ if (!addedComments.isEmpty()) {
+ addedCommentsHash = generateCommentTextHashMap(addedComments);
+ if (isCommentedOutCodeDetected(addedCommentsHash, umlOperationBodyMapper, blockBefore)) {
+ changeTypes.add(Change.Type.COMMENTED_OUT_STATEMENT);
+ }
+ }
+ return changeTypes;
+ }
+
+ private static boolean isUncommentedCodeDetected(Map deletedCommentsHash, UMLOperationBodyMapper umlOperationBodyMapper, Block blockAfter) {
+ if (deletedCommentsHash.isEmpty())
+ return false;
+ // find unmatched leaf statements afterBlock in deleted comments
+ if (!umlOperationBodyMapper.getNonMappedLeavesT2().isEmpty()) {
+ for (AbstractCodeFragment abstractCodeFragment : umlOperationBodyMapper.getNonMappedLeavesT2()) {
+ if (blockAfter.getComposite().getLeaves().contains(abstractCodeFragment)) { //if nonmappedLeave actually belongs to the target Block
+ String codeFragmentHash = Util.getSHA512(abstractCodeFragment.getString().trim());
+ if (deletedCommentsHash.containsKey(codeFragmentHash)) {
+ return true;
+ }
+ }
+ }
+ }
+ //find unmatched composite statements afterBlock in deleted comments
+ if (!umlOperationBodyMapper.getNonMappedInnerNodesT2().isEmpty()) {
+ for (CompositeStatementObject compositeStatementObject : umlOperationBodyMapper.getNonMappedInnerNodesT2()) {
+ if (((CompositeStatementObject) blockAfter.getComposite()).contains(compositeStatementObject)) { //if nonMappedInnerNode actually belongs to the current block
+ String codeFragmentHash = Util.getSHA512(compositeStatementObject.getActualSignature());
+ if (deletedCommentsHash.containsKey(codeFragmentHash)) {
+ return true;
+ }
+ }
+ }
+ }
+ // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough,
+ // even when one side is actually a commented/uncommented transition.
+ if (!umlOperationBodyMapper.getMappings().isEmpty()) {
+ for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) {
+ if (!(mapping instanceof LeafMapping)) // does not happen in composite(i guess)
+ continue;
+ if (!isMappingBelongToTrackedBlock(mapping, blockAfter, true))
+ continue;
+ //this issue only happens with leaf statements and not composites....
+ String codeFragmentHashCurrent = Util.getSHA512(mapping.getFragment2().getString().trim());
+ if (deletedCommentsHash.containsKey(codeFragmentHashCurrent)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean isCommentedOutCodeDetected(Map addedCommentsHash, UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore) {
+ if (addedCommentsHash.isEmpty())
+ return false;
+ if (!umlOperationBodyMapper.getNonMappedLeavesT1().isEmpty()) {
+ //find unmatched leaf statements parent1 in added comments
+ for (AbstractCodeFragment abstractCodeFragment : umlOperationBodyMapper.getNonMappedLeavesT1()) {
+ if (blockBefore.getComposite().getLeaves().contains(abstractCodeFragment)) { // if nonMappedleaf actually belongs to parent block
+ String codeFragmentHash = Util.getSHA512(abstractCodeFragment.getString().trim());
+ if (addedCommentsHash.containsKey(codeFragmentHash)) {
+ return true;
+ }
+ }
+ }
+ }
+ if (!umlOperationBodyMapper.getNonMappedInnerNodesT1().isEmpty()) {
+ //find unmatched composite statements parent in added comments
+ for (CompositeStatementObject compositeStatementObject : umlOperationBodyMapper.getNonMappedInnerNodesT1()) {
+ if (((CompositeStatementObject) blockBefore.getComposite()).contains(compositeStatementObject)) { // if nonMappedInnerNode actually belongs to parent block
+ if (!isBracketAndBelongsToStandAloneBlock(compositeStatementObject))
+ continue;
+ String codeFragmentHash = Util.getSHA512(compositeStatementObject.getActualSignature());
+ if (addedCommentsHash.containsKey(codeFragmentHash)) {
+ return true;
+ }
+ }
+ }
+ }
+ // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough,
+ // even when one side is actually a commented/uncommented transition.
+ if (!umlOperationBodyMapper.getMappings().isEmpty()) {
+ for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) {
+ if (!(mapping instanceof LeafMapping))
+ continue;
+ if (!isMappingBelongToTrackedBlock(mapping, blockBefore, false))
+ continue;
+ String codeFragmentHashParent = Util.getSHA512(mapping.getFragment1().getString().trim());
+ if (!addedCommentsHash.isEmpty() && addedCommentsHash.containsKey(codeFragmentHashParent)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ public static boolean isBlockBodyUnchangedDuringUncomment (UMLOperationBodyMapper umlOperationBodyMapper, CompositeStatementObject uncommentedBlock, Map deletedComments, Block blockAfter) {
+ Set mappedFragments = new HashSet<>();
+ for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) {
+ if(!isMappingBelongToTrackedBlock(mapping, blockAfter, true))
+ continue;
+ if(mapping instanceof CompositeStatementObjectMapping) {
+ CompositeStatementObject composite = (CompositeStatementObject) mapping.getFragment2();
+ if (!isBracketAndBelongsToStandAloneBlock(composite))
+ continue;
+ mappedFragments.add(Util.getSHA512(composite.getActualSignature()));
+ }
+ else if(mapping instanceof LeafMapping && mapping.getFragment2() instanceof StatementObject){ // mappings belong to the currentblock
+ mappedFragments.add(Util.getSHA512(mapping.getFragment2().getString().trim()));
+ }
+ }
+ for (CompositeStatementObject inner : uncommentedBlock.getInnerNodes()) {
+ if (!isBracketAndBelongsToStandAloneBlock(inner)) {
+ continue;
+ }
+ String hash = Util.getSHA512(inner.getActualSignature().trim());
+ if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments)){
+ return false;
+ }
+ }
+ for (AbstractCodeFragment leaf : uncommentedBlock.getLeaves()) {
+ String hash = Util.getSHA512(((StatementObject) leaf).getActualSignature());
+ if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments))
+ return false;
+ }
+ return true;
+ }
+
+ private static Change.Type partialBlockUncommented(CompositeStatementObject compositeStatementObject, Block blockBefore, Block blockAfter) {
+ //TODO: else/catch/finally uncommented support should be added
+ if (blockBefore.getComposite().getLocationInfo().getCodeElementType().equals(LocationInfo.CodeElementType.IF_STATEMENT) &&
+ blockAfter.getComposite().getLocationInfo().getCodeElementType().equals(LocationInfo.CodeElementType.IF_STATEMENT)) {
+ CompositeStatementObject ifBefore = (CompositeStatementObject) blockBefore.getComposite();
+ CompositeStatementObject ifAfter = (CompositeStatementObject) blockAfter.getComposite();
+ if (ifBefore.getStatements().size() == 1 && ifAfter.getStatements().size() == 2) {
+ return Change.Type.UNCOMMENTED_STATEMENT;
+ }
+ }
+ return null;
+ }
+
+ public static boolean isBracketAndBelongsToStandAloneBlock(CompositeStatementObject compositeStatementObject){ //heuristic
+ if ((compositeStatementObject.getLocationInfo().getCodeElementType() == LocationInfo.CodeElementType.BLOCK) && (!compositeStatementObject.getParent().getActualSignature().equals("{")))
+ return false;
+ return true;
+ }
+ private static boolean isMappingBelongToTrackedBlock(AbstractCodeMapping mapping, Block block, boolean isAfter){
+ if(mapping instanceof CompositeStatementObjectMapping){
+ CompositeStatementObject composite = (CompositeStatementObject) mapping.getFragment2();
+ if(!((CompositeStatementObject) block.getComposite()).contains(composite))
+ return false;
+ }
+ else if(mapping instanceof LeafMapping && (isAfter ? mapping.getFragment2() : mapping.getFragment1()) instanceof StatementObject){
+ if(!block.getComposite().getLeaves().contains(isAfter ? mapping.getFragment2() : mapping.getFragment1()))
+ return false;
+ }
+ return true;
+ }
+ private static boolean existsAsMappedOrComment(String hash, Set mappedFragments, Map comments){
+ return mappedFragments.contains(hash) || comments.containsKey(hash);
+ }
+ public static String normalizeCommentText(String commentText) {
+ if (commentText.startsWith("//")) {
+ commentText = commentText.substring(2);
+ }
+ commentText = commentText.trim();
+ return commentText;
+ }
+
+ public static Map generateCommentTextHashMap(List commentList) {
+ Map commentTextHashMap = new HashMap<>();
+ for (UMLComment comment : commentList) {
+ String text = comment.getText();
+ // remove /* */
+ text = text.replace("/*", "").replace("*/", "");
+ String[] lines = text.split("\\R");
+ for (String line : lines) {
+ line = normalizeCommentText(line);
+ if (line.isBlank()) {
+ continue;
+ }
+ commentTextHashMap.put(Util.getSHA512(line), comment);
+ }
+ }
+ return commentTextHashMap;
+ }
+
+}
From 2fda0750c3ba1d954edac10a7a548b6860cce873 Mon Sep 17 00:00:00 2001
From: parinaz-st
Date: Fri, 5 Jun 2026 21:49:52 +0330
Subject: [PATCH 3/5] - Added scope boundary checking for comments - Removed
heuristic for handling comments in similar matched statements
---
.../util/CommentTransitionAnalyzer.java | 51 +++++++------------
1 file changed, 18 insertions(+), 33 deletions(-)
diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
index 311466a9f48..a9872a470fa 100644
--- a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
+++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
@@ -19,19 +19,33 @@ public static Set DetectCommentTransitionsInsideMatchedBlock(UMLOpe
Map addedCommentsHash;
if (!deletedComments.isEmpty()) {
- deletedCommentsHash = generateCommentTextHashMap(deletedComments);
- if (isUncommentedCodeDetected(deletedCommentsHash, umlOperationBodyMapper, blockAfter))
+ List inScopeDeletedComments;
+ inScopeDeletedComments = removeOutOfScopeComments(deletedComments, blockBefore);
+ deletedCommentsHash = generateCommentTextHashMap(inScopeDeletedComments);
+ if (isUncommentedCodeDetected(deletedCommentsHash, umlOperationBodyMapper, blockAfter)) {
changeTypes.add(Change.Type.UNCOMMENTED_STATEMENT);
+ }
}
if (!addedComments.isEmpty()) {
- addedCommentsHash = generateCommentTextHashMap(addedComments);
+ List inScopeAddedComments;
+ inScopeAddedComments = removeOutOfScopeComments(addedComments, blockAfter);
+ addedCommentsHash = generateCommentTextHashMap(inScopeAddedComments);
if (isCommentedOutCodeDetected(addedCommentsHash, umlOperationBodyMapper, blockBefore)) {
changeTypes.add(Change.Type.COMMENTED_OUT_STATEMENT);
}
}
return changeTypes;
}
-
+ private static List removeOutOfScopeComments(List commentList, Block block){
+ List inScopeComments = new ArrayList<>();
+ for (int i = 0; i < commentList.size(); i++) {
+ if ((commentList.get(i).getLocationInfo().getStartLine() > block.getLocation().getStartLine() &&
+ commentList.get(i).getLocationInfo().getEndLine() < block.getLocation().getEndLine())) {
+ inScopeComments.add(commentList.get(i));
+ }
+ }
+ return inScopeComments;
+ }
private static boolean isUncommentedCodeDetected(Map deletedCommentsHash, UMLOperationBodyMapper umlOperationBodyMapper, Block blockAfter) {
if (deletedCommentsHash.isEmpty())
return false;
@@ -57,21 +71,6 @@ private static boolean isUncommentedCodeDetected(Map deleted
}
}
}
- // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough,
- // even when one side is actually a commented/uncommented transition.
- if (!umlOperationBodyMapper.getMappings().isEmpty()) {
- for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) {
- if (!(mapping instanceof LeafMapping)) // does not happen in composite(i guess)
- continue;
- if (!isMappingBelongToTrackedBlock(mapping, blockAfter, true))
- continue;
- //this issue only happens with leaf statements and not composites....
- String codeFragmentHashCurrent = Util.getSHA512(mapping.getFragment2().getString().trim());
- if (deletedCommentsHash.containsKey(codeFragmentHashCurrent)) {
- return true;
- }
- }
- }
return false;
}
@@ -102,20 +101,6 @@ private static boolean isCommentedOutCodeDetected(Map addedC
}
}
}
- // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough,
- // even when one side is actually a commented/uncommented transition.
- if (!umlOperationBodyMapper.getMappings().isEmpty()) {
- for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) {
- if (!(mapping instanceof LeafMapping))
- continue;
- if (!isMappingBelongToTrackedBlock(mapping, blockBefore, false))
- continue;
- String codeFragmentHashParent = Util.getSHA512(mapping.getFragment1().getString().trim());
- if (!addedCommentsHash.isEmpty() && addedCommentsHash.containsKey(codeFragmentHashParent)) {
- return true;
- }
- }
- }
return false;
}
public static boolean isBlockBodyUnchangedDuringUncomment (UMLOperationBodyMapper umlOperationBodyMapper, CompositeStatementObject uncommentedBlock, Map deletedComments, Block blockAfter) {
From 7e3fd6015eb7ee88faafa92cd237aa9119fe8a13 Mon Sep 17 00:00:00 2001
From: parinaz-st
Date: Fri, 12 Jun 2026 14:57:41 +0330
Subject: [PATCH 4/5] Refine the comment-transition detection heuristic by
applying it only to leaf mappings with replacements
---
.../util/CommentTransitionAnalyzer.java | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
index a9872a470fa..b8a6474cf05 100644
--- a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
+++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
@@ -71,6 +71,22 @@ private static boolean isUncommentedCodeDetected(Map deleted
}
}
}
+ // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough,
+ // even when one side is actually a commented/uncommented transition.
+ if (!umlOperationBodyMapper.getMappings().isEmpty()) {
+ for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) {
+ if (!(mapping instanceof LeafMapping)) // does not happen in composite
+ continue;
+ if(mapping.getReplacements().isEmpty()) //real mapping
+ continue;
+ if (!isMappingBelongToTrackedBlock(mapping, blockAfter, true))
+ continue;
+ String codeFragmentHashCurrent = Util.getSHA512(mapping.getFragment2().getString().trim());
+ if (deletedCommentsHash.containsKey(codeFragmentHashCurrent)) {
+ return true;
+ }
+ }
+ }
return false;
}
@@ -101,6 +117,22 @@ private static boolean isCommentedOutCodeDetected(Map addedC
}
}
}
+ // RefactoringMiner may create leaf mappings between semantically different statements when textual similarity is high enough,
+ // even when one side is actually a commented/uncommented transition.
+ if (!umlOperationBodyMapper.getMappings().isEmpty()) {
+ for (AbstractCodeMapping mapping : umlOperationBodyMapper.getMappings()) {
+ if (!(mapping instanceof LeafMapping)) // does not happen in composite
+ continue;
+ if(mapping.getReplacements().isEmpty()) //real mapping
+ continue;
+ if (!isMappingBelongToTrackedBlock(mapping, blockBefore, false))
+ continue;
+ String codeFragmentHashParent = Util.getSHA512(mapping.getFragment1().getString().trim());
+ if (!addedCommentsHash.isEmpty() && addedCommentsHash.containsKey(codeFragmentHashParent)) {
+ return true;
+ }
+ }
+ }
return false;
}
public static boolean isBlockBodyUnchangedDuringUncomment (UMLOperationBodyMapper umlOperationBodyMapper, CompositeStatementObject uncommentedBlock, Map deletedComments, Block blockAfter) {
From 7c8d874576a099e510e972bbde9e54c1470afc0b Mon Sep 17 00:00:00 2001
From: parinaz-st
Date: Thu, 9 Jul 2026 00:45:58 +0330
Subject: [PATCH 5/5] Support detection of multiline commented-out block
signatures
---
.../BlockTrackerChangeHistory.java | 17 +-
.../util/CommentTransitionAnalyzer.java | 182 +++++++++++++++++-
2 files changed, 185 insertions(+), 14 deletions(-)
diff --git a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
index 2743ff53f90..10d97298bcb 100644
--- a/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
+++ b/src/main/java/org/codetracker/BlockTrackerChangeHistory.java
@@ -682,22 +682,27 @@ public boolean isBlockUncommented(UMLOperationBodyMapper umlOperationBodyMapper,
List deletedComments = umlCommentListDiff.getDeletedComments();
if(currentNonMappedInnerNodes.size() == 0 || deletedComments == null || deletedComments.isEmpty())
return false;
- Map deletedCommentsHashMap = generateCommentTextHashMap(deletedComments);
+ //reconstruct multiline block signature
+ Map allDeletedCommentsHashMap = generateCommentTextHashMap(deletedComments);
+ Map deletedCommentsBlockSignatureHashMap = generateUnCommentedBlockSignaturetHashMap(deletedComments);
+ if(deletedCommentsBlockSignatureHashMap.isEmpty())
+ return false;
for(CompositeStatementObject nonMappedInnerNode : currentNonMappedInnerNodes) {
- String hashOfNonMappedInnerNode = Util.getSHA512(nonMappedInnerNode.getActualSignature());
- if (!deletedCommentsHashMap.containsKey(hashOfNonMappedInnerNode))
+ String normalizedSignature = nonMappedInnerNode.getActualSignature().replaceAll("\\s+", " ").trim();
+ String hashOfNonMappedInnerNode = Util.getSHA512(normalizedSignature);
+ if (!deletedCommentsBlockSignatureHashMap.containsKey(hashOfNonMappedInnerNode))
continue;
Block blockAfter = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), currentVersion);
if (!equalOperator.test(blockAfter))
continue;
//check block body continuity
- if (!isBlockBodyUnchangedDuringUncomment(umlOperationBodyMapper, nonMappedInnerNode, deletedCommentsHashMap, blockAfter))
+ if (!isBlockBodyUnchangedDuringUncomment(umlOperationBodyMapper, nonMappedInnerNode, allDeletedCommentsHashMap, blockAfter))
continue;
Block virtualBlockBefore = Block.of(nonMappedInnerNode, umlOperationBodyMapper.getContainer2(), parentVersion); // add metadata later, flag isVirtual = true;
blockChangeHistory.addChange(virtualBlockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.UNCOMMENTED_BLOCK));
elements.add(virtualBlockBefore);
blockChangeHistory.connectRelatedNodes();
- return true;
+ return true;
}
return false;
}
@@ -1142,7 +1147,7 @@ public boolean isMatched(UMLOperationBodyMapper umlOperationBodyMapper, Version
List stringRepresentationBodyBefore = stringRepresentationBefore.subList(1, stringRepresentationBefore.size());
List stringRepresentationBodyAfter = stringRepresentationAfter.subList(1, stringRepresentationAfter.size());
if (!stringRepresentationBodyBefore.equals(stringRepresentationBodyAfter)) {
- Set commentTransitions = DetectCommentTransitionsInsideMatchedBlock(umlOperationBodyMapper, blockBefore, blockAfter, currentVersion, parentVersion);
+ Set commentTransitions = detectCommentTransitionsInsideMatchedBlock(umlOperationBodyMapper, blockBefore, blockAfter, currentVersion, parentVersion);
if(!commentTransitions.isEmpty()){
for(Change.Type changeType : commentTransitions){
blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(changeType));
diff --git a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
index b8a6474cf05..e33c4941e63 100644
--- a/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
+++ b/src/main/java/org/codetracker/util/CommentTransitionAnalyzer.java
@@ -11,7 +11,7 @@
public class CommentTransitionAnalyzer {
- public static Set DetectCommentTransitionsInsideMatchedBlock(UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore, Block blockAfter, Version currentVersion, Version parentVersion) {
+ public static Set detectCommentTransitionsInsideMatchedBlock(UMLOperationBodyMapper umlOperationBodyMapper, Block blockBefore, Block blockAfter, Version currentVersion, Version parentVersion) {
Set changeTypes = new HashSet<>();
List deletedComments = umlOperationBodyMapper.getCommentListDiff().getDeletedComments();
List addedComments = umlOperationBodyMapper.getCommentListDiff().getAddedComments();
@@ -151,16 +151,19 @@ else if(mapping instanceof LeafMapping && mapping.getFragment2() instanceof Stat
}
}
for (CompositeStatementObject inner : uncommentedBlock.getInnerNodes()) {
+ if(inner.getActualSignature().equals(uncommentedBlock.getActualSignature())){
+ continue;
+ }
if (!isBracketAndBelongsToStandAloneBlock(inner)) {
continue;
}
- String hash = Util.getSHA512(inner.getActualSignature().trim());
+ String hash = Util.getSHA512(inner.getActualSignature().replaceAll("\\s+", " ").trim());
if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments)){
return false;
}
}
for (AbstractCodeFragment leaf : uncommentedBlock.getLeaves()) {
- String hash = Util.getSHA512(((StatementObject) leaf).getActualSignature());
+ String hash = Util.getSHA512(((StatementObject) leaf).getActualSignature().replaceAll("\\s+", " ").trim());
if(!existsAsMappedOrComment(hash, mappedFragments, deletedComments))
return false;
}
@@ -197,23 +200,35 @@ else if(mapping instanceof LeafMapping && (isAfter ? mapping.getFragment2() : ma
}
return true;
}
- private static boolean existsAsMappedOrComment(String hash, Set mappedFragments, Map comments){
- return mappedFragments.contains(hash) || comments.containsKey(hash);
+ private static boolean existsAsMappedOrComment(String hash, Set mappedFragments, Map commentsHash){
+ return mappedFragments.contains(hash) || commentsHash.containsKey(hash);
}
public static String normalizeCommentText(String commentText) {
+ commentText = commentText.trim();
if (commentText.startsWith("//")) {
commentText = commentText.substring(2);
}
+ if (commentText.startsWith("/*")) {
+ commentText = commentText.substring(2);
+ }
+ if (commentText.endsWith("*/")) {
+ commentText = commentText.substring(0, commentText.length() - 2);
+ }
commentText = commentText.trim();
- return commentText;
+ if (commentText.startsWith("*")) {
+ commentText = commentText.substring(1);
+ }
+ return commentText.trim();
}
public static Map generateCommentTextHashMap(List commentList) {
Map commentTextHashMap = new HashMap<>();
for (UMLComment comment : commentList) {
String text = comment.getText();
- // remove /* */
- text = text.replace("/*", "").replace("*/", "");
+ text = normalizeCommentText(text);
+ if(comment.getFullText().startsWith("/*")){
+ commentTextHashMap.put(Util.getSHA512(text.replaceAll("\\s+", " ").trim()), comment);
+ }
String[] lines = text.split("\\R");
for (String line : lines) {
line = normalizeCommentText(line);
@@ -225,5 +240,156 @@ public static Map generateCommentTextHashMap(List generateUnCommentedBlockSignaturetHashMap(List commentList) {
+ Map blockSignatureUnCommentedHashMap = new HashMap<>();
+ List blockSignatureUnCommented = extractBlockSignaturesFromComments(commentList);
+ for(String signature : blockSignatureUnCommented){
+ String normalized = signature.replaceAll("\\s+", " ").trim();
+ blockSignatureUnCommentedHashMap.put(Util.getSHA512(normalized), normalized);
+ }
+ return blockSignatureUnCommentedHashMap;
+ }
+public static List extractBlockSignaturesFromComments(List comments) {
+ List signatures = new ArrayList<>();
+
+ for (int i = 0; i < comments.size(); i++) {
+ UMLComment comment = comments.get(i);
+ String text = normalizeCommentText(comment.getText());
+ if (text.isBlank())
+ continue;
+ if (isLeafStatement(text))
+ continue;
+ // Complete one-line signature
+ if (isCompleteBlockSignature(text)) {
+ signatures.add(text);
+ continue;
+ }
+ if (comment.getFullText().startsWith("/*")) {
+ signatures.addAll(extractFromBlockComment(text));
+ }
+ else if (comment.getFullText().startsWith("//")) {
+ if (!hasBlockKeyword(text))
+ continue;
+ SingleLineExtractionResult singleLineExtractionResult = extractFromSingleLineComments(comments, i, text);
+ signatures.addAll(singleLineExtractionResult.getSignatures());
+ i = singleLineExtractionResult.getLastConsumedIndex();
+ }
+ }
+ return signatures;
+}
+private static boolean isCompleteBlockSignature(String text) {
+ return hasBlockKeyword(text) && text.trim().endsWith("{");
+ }
+ private static boolean signatureEnds(String text) {
+ return text.trim().endsWith("{");
+ }
+ private static boolean hasBlockKeyword(String text) {
+ String trimmed = text.trim();
+ return trimmed.startsWith("if")
+ || trimmed.startsWith("else")
+ || trimmed.startsWith("for")
+ || trimmed.startsWith("while")
+ || trimmed.startsWith("do")
+ || trimmed.startsWith("try")
+ || trimmed.startsWith("catch")
+ || trimmed.startsWith("finally")
+ || trimmed.startsWith("switch")
+ || trimmed.startsWith("synchronized")
+ || trimmed.startsWith("static");
+ //TODO:Add support for standalone block {}
+ }
+ private static boolean areConsecutiveComments(
+ UMLComment current,
+ UMLComment next) {
+
+ return next.getLocationInfo().getStartLine()
+ == current.getLocationInfo().getEndLine() + 1;
+ }
+ private static boolean isLeafStatement(String text) {
+ return (!hasBlockKeyword(text) && text.trim().endsWith(";"));
+ }
+
+ private static List extractFromBlockComment(String commentText) {
+ List signatures = new ArrayList<>();
+ String[] lines = commentText.split("\\R");
+ for (int j = 0; j < lines.length; j++) {
+ if (isLeafStatement(lines[j]))
+ continue;
+ if (!hasBlockKeyword(lines[j]))
+ continue;
+ StringBuilder signatureBuilder = new StringBuilder();
+ signatureBuilder.append(lines[j]);
+ int currentIndex = j + 1;
+ while (!signatureEnds(signatureBuilder.toString())
+ && currentIndex < lines.length) {
+ String nextLine = lines[currentIndex];
+ if (isLeafStatement(nextLine)) {
+ if (signatureBuilder.toString().trim().endsWith(")")) {
+ signatures.add(signatureBuilder.toString());
+ }
+ break;
+ }
+ // another block signature starts
+ if (hasBlockKeyword(nextLine))
+ break;
+ signatureBuilder.append("\n").append(nextLine);
+ currentIndex++;
+ }
+ if (signatureEnds(signatureBuilder.toString())) {
+ signatures.add(signatureBuilder.toString());
+ }
+ j = currentIndex - 1;
+ }
+ return signatures;
+ }
+
+ private static SingleLineExtractionResult extractFromSingleLineComments(List comments, int index, String commentText){
+ int lastConsumedIndex = index;
+ List signatures = new ArrayList<>();
+ StringBuilder signatureBuilder = new StringBuilder();
+ signatureBuilder.append(commentText);
+ int currentIndex = index;
+ while (!signatureEnds(signatureBuilder.toString())
+ && currentIndex + 1 < comments.size()) {
+ UMLComment current = comments.get(currentIndex);
+ UMLComment next = comments.get(currentIndex + 1);
+ if (!areConsecutiveComments(current, next))
+ break;
+ String nextText = normalizeCommentText(next.getText());
+ if (isLeafStatement(nextText)) {
+ if (signatureBuilder.toString().trim().endsWith(")")) {
+ signatures.add(signatureBuilder.toString());
+ }
+ break;
+ }
+ // another block signature starts inside the same comment
+ if (hasBlockKeyword(nextText))
+ break;
+ signatureBuilder.append("\n").append(nextText);
+ currentIndex++;
+ }
+ if (signatureEnds(signatureBuilder.toString())) {
+ signatures.add(signatureBuilder.toString());
+ lastConsumedIndex = currentIndex;
+ }
+ return new SingleLineExtractionResult(signatures, lastConsumedIndex);
+ }
+ private static class SingleLineExtractionResult {
+ private List signatures;
+ private int lastConsumedIndex;
+ SingleLineExtractionResult(List signatures,
+ int lastConsumedIndex) {
+ this.signatures = signatures;
+ this.lastConsumedIndex = lastConsumedIndex;
+ }
+ public List getSignatures() {
+ return signatures;
+ }
+
+ public int getLastConsumedIndex() {
+ return lastConsumedIndex;
+ }
+ }
}
+