From 0c9eb5ff17d3bab7941e37c1bf5b10ede446dc20 Mon Sep 17 00:00:00 2001
From: parinaz-st
Date: Thu, 28 May 2026 19:20:49 +0330
Subject: [PATCH 1/3] 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 2/3] - 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 3/3] 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) {