Skip to content

Commit

Permalink
Support multi-line statements in FileTracker
Browse files Browse the repository at this point in the history
  • Loading branch information
tsantalis committed Oct 17, 2024
1 parent fcb7554 commit 10344c4
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 39 deletions.
149 changes: 145 additions & 4 deletions src/main/java/org/codetracker/BlockTrackerChangeHistory.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package org.codetracker;

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.function.Predicate;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.codetracker.api.History;
import org.codetracker.api.History.HistoryInfo;
Expand All @@ -16,6 +22,7 @@
import org.codetracker.change.Change;
import org.codetracker.change.ChangeFactory;
import org.codetracker.change.Introduced;
import org.codetracker.change.block.BlockSignatureFormatChange;
import org.codetracker.change.block.ElseBlockAdded;
import org.codetracker.change.block.ExpressionChange;
import org.codetracker.change.block.MergeBlock;
Expand All @@ -28,6 +35,12 @@
import org.codetracker.element.Method;
import org.refactoringminer.api.Refactoring;

import com.github.difflib.DiffUtils;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.Chunk;
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;
Expand Down Expand Up @@ -224,7 +237,8 @@ else if (mapping instanceof LeafMapping && mapping.getFragment1() instanceof Sta
List<String> stringRepresentationBefore = blockBefore.getComposite().stringRepresentation();
List<String> stringRepresentationAfter = matchedBlockInsideExtractedMethodBody.getComposite().stringRepresentation();
if (!stringRepresentationBefore.equals(stringRepresentationAfter)) {
blockChangeHistory.addChange(blockBefore, matchedBlockInsideExtractedMethodBody, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
//blockChangeHistory.addChange(blockBefore, matchedBlockInsideExtractedMethodBody, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
addStatementChange(blockBefore, matchedBlockInsideExtractedMethodBody);
}
break;
}
Expand Down Expand Up @@ -284,7 +298,8 @@ else if (mapping instanceof LeafMapping && mapping.getFragment1() instanceof Sta
List<String> stringRepresentationBefore = blockBefore.getComposite().stringRepresentation();
List<String> stringRepresentationAfter = matchedBlockInsideExtractedMethodBody.getComposite().stringRepresentation();
if (!stringRepresentationBefore.equals(stringRepresentationAfter)) {
blockChangeHistory.addChange(blockBefore, matchedBlockInsideExtractedMethodBody, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
//blockChangeHistory.addChange(blockBefore, matchedBlockInsideExtractedMethodBody, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
addStatementChange(blockBefore, matchedBlockInsideExtractedMethodBody);
}
break;
}
Expand Down Expand Up @@ -785,10 +800,16 @@ else if (mapping instanceof LeafMapping && mapping.getFragment1() instanceof Sta
if (blockAfter != null && equalOperator.test(blockAfter)) {
Block blockBefore = Block.of((StatementObject) mapping.getFragment1(), umlOperationBodyMapper.getContainer1(), parentVersion);
if (!blockBefore.getComposite().toString().equals(blockAfter.getComposite().toString())) {
blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
addStatementChange(blockBefore, blockAfter);
}
else {
blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.of(AbstractChange.Type.NO_CHANGE));
if(blockBefore.differInFormatting(blockAfter)) {
blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.SIGNATURE_FORMAT_CHANGE));
processChange(blockBefore, blockAfter);
}
else {
blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.of(AbstractChange.Type.NO_CHANGE));
}
}
if(matches == 0) {
elements.add(blockBefore);
Expand All @@ -804,6 +825,11 @@ else if (mapping instanceof LeafMapping && mapping.getFragment1() instanceof Sta
return false;
}

private void addStatementChange(Block blockBefore, Block blockAfter) {
blockChangeHistory.addChange(blockBefore, blockAfter, ChangeFactory.forBlock(Change.Type.BODY_CHANGE));
processChange(blockBefore, blockAfter);
}

public void addedMethod(Method rightMethod, Block rightBlock, Version parentVersion) {
Block blockBefore = Block.of(rightBlock.getComposite(), rightMethod.getUmlOperation(), parentVersion);
blockChangeHistory.handleAdd(blockBefore, rightBlock, "added with method");
Expand Down Expand Up @@ -898,6 +924,82 @@ public boolean checkRefactoredMethod(Version currentVersion, Version parentVersi
return false;
}

private Map<Pair<Block, Block>, List<Integer>> lineChangeMap = new LinkedHashMap<>();

public void processChange(Block blockBefore, Block blockAfter) {
if (blockBefore.isMultiLine() || blockAfter.isMultiLine()) {
try {
Pair<Block, Block> pair = Pair.of(blockBefore, blockAfter);
Block startBlock = getStart();
if (startBlock != null && startBlock.isMultiLine()) {
List<String> start = IOUtils.readLines(new StringReader(((StatementObject)startBlock.getComposite()).getActualSignature()));
List<String> original = IOUtils.readLines(new StringReader(((StatementObject)blockBefore.getComposite()).getActualSignature()));
List<String> revised = IOUtils.readLines(new StringReader(((StatementObject)blockAfter.getComposite()).getActualSignature()));

Patch<String> patch = DiffUtils.diff(original, revised);
List<AbstractDelta<String>> deltas = patch.getDeltas();
for (int i=0; i<deltas.size(); i++) {
AbstractDelta<String> delta = deltas.get(i);
Chunk<String> target = delta.getTarget();
List<String> affectedLines = new ArrayList<>(target.getLines());
boolean subListFound = false;
if (affectedLines.size() > 1 && !(delta instanceof InsertDelta)) {
int index = Collections.indexOfSubList(start, affectedLines);
if (index != -1) {
subListFound = true;
for (int j=0; j<affectedLines.size(); j++) {
int actualLine = startBlock.signatureStartLine() + index + j;
if (lineChangeMap.containsKey(pair)) {
lineChangeMap.get(pair).add(actualLine);
}
else {
List<Integer> list = new ArrayList<>();
list.add(actualLine);
lineChangeMap.put(pair, list);
}
}
}
}
if (!subListFound) {
for (String line : affectedLines) {
List<Integer> matchingIndices = findAllMatchingIndices(start, line);
for (Integer index : matchingIndices) {
if (original.size() > index && revised.size() > index &&
original.get(index).equals(line) && revised.get(index).equals(line)) {
continue;
}
int actualLine = startBlock.signatureStartLine() + index;
if (lineChangeMap.containsKey(pair)) {
lineChangeMap.get(pair).add(actualLine);
}
else {
List<Integer> list = new ArrayList<>();
list.add(actualLine);
lineChangeMap.put(pair, list);
}
break;
}
}
}
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
}

private List<Integer> findAllMatchingIndices(List<String> startCommentLines, String line) {
List<Integer> matchingIndices = new ArrayList<>();
for(int i=0; i<startCommentLines.size(); i++) {
String element = startCommentLines.get(i).trim();
if(line.equals(element) || element.contains(line.trim())) {
matchingIndices.add(i);
}
}
return matchingIndices;
}

public HistoryInfo<Block> blameReturn(Block startBlock) {
List<HistoryInfo<Block>> history = getHistory();
for (History.HistoryInfo<Block> historyInfo : history) {
Expand Down Expand Up @@ -925,4 +1027,43 @@ else if (startBlock.isClosingCurlyBracket()) {
}
return null;
}

public HistoryInfo<Block> blameReturn(Block startBlock, int exactLineNumber) {
List<HistoryInfo<Block>> history = getHistory();
for (History.HistoryInfo<Block> historyInfo : history) {
Pair<Block, Block> pair = Pair.of(historyInfo.getElementBefore(), historyInfo.getElementAfter());
boolean multiLine = startBlock.isMultiLine();
for (Change change : historyInfo.getChangeList()) {
if (startBlock.isElseBlockStart() || startBlock.isElseBlockEnd()) {
if (change instanceof Introduced || change instanceof ElseBlockAdded) {
return historyInfo;
}
}
else if (startBlock.isClosingCurlyBracket()) {
if (change instanceof Introduced || change instanceof ReplacePipelineWithLoop) {
return historyInfo;
}
}
else {
if (change instanceof ExpressionChange || change instanceof Introduced || change instanceof MergeBlock || change instanceof SplitBlock ||
change instanceof ReplaceLoopWithPipeline || change instanceof ReplacePipelineWithLoop || change instanceof ReplaceConditionalWithTernary) {
return historyInfo;
}
if (startBlock.getComposite() instanceof StatementObject && (change instanceof BodyChange || change instanceof BlockSignatureFormatChange)) {
if (multiLine) {
if (lineChangeMap.containsKey(pair)) {
if (lineChangeMap.get(pair).contains(exactLineNumber)) {
return historyInfo;
}
}
}
else {
return historyInfo;
}
}
}
}
}
return null;
}
}
8 changes: 7 additions & 1 deletion src/main/java/org/codetracker/FileTrackerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ else if (startElement instanceof Block) {
startBlock.checkElseBlockStart(lineNumber);
startBlock.checkElseBlockEnd(lineNumber);
startBlock.checkClosingBracketOfAnonymousClassDeclaration(lineNumber);
HistoryInfo<Block> historyInfo = startBlockChangeHistory.blameReturn(startBlock);
HistoryInfo<Block> historyInfo = startBlockChangeHistory.blameReturn(startBlock, lineNumber);
blameInfo.put(lineNumber, historyInfo);
}
else if (startElement instanceof Class) {
Expand Down Expand Up @@ -1561,6 +1561,12 @@ private Map<Method, MethodTrackerChangeHistory> processMethodsWithSameSignature(
}
}
startMethodChangeHistory.setCurrent(leftMethod);
int leftLines = leftMethod.getLocation().getEndLine() - leftMethod.getLocation().getStartLine();
int rightLines = rightMethod.getLocation().getEndLine() - rightMethod.getLocation().getStartLine();
if (leftLines != rightLines) {
processNestedStatementsAndComments(rightModel, currentVersion, leftModel, parentVersion,
startMethod, rightMethod, leftMethod);
}
continue;
}
//CHANGE BODY OR DOCUMENT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ else if (startElement instanceof Block) {
startBlock.checkElseBlockStart(lineNumber);
startBlock.checkElseBlockEnd(lineNumber);
startBlock.checkClosingBracketOfAnonymousClassDeclaration(lineNumber);
HistoryInfo<Block> historyInfo = startBlockChangeHistory.blameReturn(startBlock);
HistoryInfo<Block> historyInfo = startBlockChangeHistory.blameReturn(startBlock, lineNumber);
blameInfo.put(lineNumber, historyInfo);
}
else if (startElement instanceof Class) {
Expand Down Expand Up @@ -1568,6 +1568,12 @@ private Map<Method, MethodTrackerChangeHistory> processMethodsWithSameSignature(
}
}
startMethodChangeHistory.setCurrent(leftMethod);
int leftLines = leftMethod.getLocation().getEndLine() - leftMethod.getLocation().getStartLine();
int rightLines = rightMethod.getLocation().getEndLine() - rightMethod.getLocation().getStartLine();
if (leftLines != rightLines) {
processNestedStatementsAndComments(rightModel, currentVersion, leftModel, parentVersion,
startMethod, rightMethod, leftMethod);
}
continue;
}
//CHANGE BODY OR DOCUMENT
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.codetracker.change.block;

public class BlockSignatureFormatChange extends BlockChange {

public BlockSignatureFormatChange() {
super(Type.SIGNATURE_FORMAT_CHANGE);
}

}
24 changes: 24 additions & 0 deletions src/main/java/org/codetracker/element/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,30 @@ public static Block of(AbstractStatement statement, Method method) {
return of((CompositeStatementObject) statement, method);
}

public boolean differInFormatting(Block other) {
if (composite instanceof StatementObject && other.composite instanceof StatementObject) {
String thisSignature = ((StatementObject) composite).getActualSignature();
String otherSignature = ((StatementObject) other.composite).getActualSignature();
if (thisSignature != null && otherSignature != null) {
int leftLines = this.getLocation().getEndLine() - this.getLocation().getStartLine();
int rightLines = other.getLocation().getEndLine() - other.getLocation().getStartLine();
return !thisSignature.equals(otherSignature) && leftLines != rightLines && thisSignature.replaceAll("\\s+","").equals(otherSignature.replaceAll("\\s+",""));
}
}
return false;
}

public int signatureStartLine() {
return composite.getLocationInfo().getStartLine();
}

public boolean isMultiLine() {
if (composite instanceof StatementObject && ((StatementObject)composite).getActualSignature() != null) {
return ((StatementObject)composite).getActualSignature().contains("\n");
}
return false;
}

public void checkClosingBracketOfAnonymousClassDeclaration(int lineNumber) {
if (getComposite() instanceof StatementObject && getComposite().getAnonymousClassDeclarations().size() > 0 && getLocation().getEndLine() == lineNumber) {
setClosingCurlyBracket(true);
Expand Down
Loading

0 comments on commit 10344c4

Please sign in to comment.