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) {