From 08672ca61ebc3c9c5c45a932048645a0872bb077 Mon Sep 17 00:00:00 2001 From: tsantalis Date: Sun, 18 Aug 2024 07:34:35 -0400 Subject: [PATCH] Bug fix when class is added (attributes + inner classes) --- .../java/org/codetracker/FileTrackerImpl.java | 70 ++++++++++++++++++- .../FileTrackerWithLocalFilesImpl.java | 70 ++++++++++++++++++- .../CodeTrackerBlameWithLocalFilesTest.java | 48 +++++++++++++ 3 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 src/test/java/org/codetracker/blame/CodeTrackerBlameWithLocalFilesTest.java diff --git a/src/main/java/org/codetracker/FileTrackerImpl.java b/src/main/java/org/codetracker/FileTrackerImpl.java index 2d151df0d64..e16986131ab 100644 --- a/src/main/java/org/codetracker/FileTrackerImpl.java +++ b/src/main/java/org/codetracker/FileTrackerImpl.java @@ -370,7 +370,8 @@ else if (leftSuperclass == null && rightSuperclass != null) { if (startClassChangeHistory.isClassAdded(umlModelDiffAll, currentVersion, parentVersion, rightClass::equalIdentifierIgnoringVersion)) { processAddedMethods(umlModelPairAll.getRight(), umlModelDiffAll, currentVersion, parentVersion); - processAddedAttributes(umlModelDiffAll, currentVersion, parentVersion); + processAddedAttributes(umlModelPairAll.getRight(), umlModelDiffAll, currentVersion, parentVersion); + processAddedInnerClasses(umlModelPairAll.getRight(), umlModelDiffAll, currentVersion, parentVersion, startClass); processAddedImportsAndClassComments(rightClass, parentVersion); break; } @@ -513,7 +514,62 @@ else if (key instanceof Comment) { } } - private void processAddedAttributes(UMLModelDiff umlModelDiffAll, Version currentVersion, Version parentVersion) { + private void processAddedInnerClasses(UMLModel rightModel, UMLModelDiff umlModelDiffAll, Version currentVersion, Version parentVersion, Class startClass) { + for (CodeElement key : programElementMap.keySet()) { + if (key instanceof Class && !key.equals(startClass)) { + Class startInnerClass = (Class)key; + ClassTrackerChangeHistory startInnerClassChangeHistory = (ClassTrackerChangeHistory) programElementMap.get(startInnerClass); + Class currentClass = startInnerClassChangeHistory.peek(); + if (currentClass == null) { + currentClass = startInnerClassChangeHistory.getCurrent(); + } + if (currentClass == null || currentClass.isAdded()) { + continue; + } + Class rightClass = getClass(rightModel, currentVersion, currentClass::equalIdentifierIgnoringVersion); + if (rightClass == null) { + continue; + } + startInnerClassChangeHistory.poll(); + if (startInnerClassChangeHistory.isClassAdded(umlModelDiffAll, currentVersion, parentVersion, rightClass::equalIdentifierIgnoringVersion)) { + for (CodeElement key2 : programElementMap.keySet()) { + if (key2 instanceof Comment) { + Comment startComment = (Comment)key2; + CommentTrackerChangeHistory startCommentChangeHistory = (CommentTrackerChangeHistory) programElementMap.get(startComment); + if ((startComment.getClazz().isPresent() && startComment.getClazz().get().equals(startInnerClass.getUmlClass())) || + (!startCommentChangeHistory.isEmpty() && startCommentChangeHistory.peek().getClazz().isPresent() && rightClass.getUmlClass().equals(startCommentChangeHistory.peek().getClazz().get()))) { + Comment currentComment = startCommentChangeHistory.poll(); + Comment rightComment = rightClass.findComment(currentComment::equalIdentifierIgnoringVersion); + if (rightComment != null) { + Comment commentBefore = Comment.of(rightComment.getComment(), rightComment.getClazz().get(), parentVersion); + startCommentChangeHistory.get().handleAdd(commentBefore, rightComment, "added with inner class"); + startCommentChangeHistory.add(commentBefore); + startCommentChangeHistory.get().connectRelatedNodes(); + } + } + } + else if (key2 instanceof Annotation) { + Annotation startAnnotation = (Annotation)key2; + AnnotationTrackerChangeHistory startAnnotationChangeHistory = (AnnotationTrackerChangeHistory) programElementMap.get(startAnnotation); + if ((startAnnotation.getClazz().isPresent() && startAnnotation.getClazz().get().equals(startInnerClass.getUmlClass())) || + (!startAnnotationChangeHistory.isEmpty() && startAnnotationChangeHistory.peek().getClazz().isPresent() && rightClass.getUmlClass().equals(startAnnotationChangeHistory.peek().getClazz().get()))) { + Annotation currentAnnotation = startAnnotationChangeHistory.poll(); + Annotation rightAnnotation = rightClass.findAnnotation(currentAnnotation::equalIdentifierIgnoringVersion); + if (rightAnnotation != null) { + Annotation annotationBefore = Annotation.of(rightAnnotation.getAnnotation(), rightAnnotation.getClazz().get(), parentVersion); + startAnnotationChangeHistory.get().handleAdd(annotationBefore, rightAnnotation, "added with inner class"); + startAnnotationChangeHistory.add(annotationBefore); + startAnnotationChangeHistory.get().connectRelatedNodes(); + } + } + } + } + } + } + } + } + + private void processAddedAttributes(UMLModel rightModel, UMLModelDiff umlModelDiffAll, Version currentVersion, Version parentVersion) { for (CodeElement key : programElementMap.keySet()) { if (key instanceof Attribute) { Attribute startAttribute = (Attribute)key; @@ -525,7 +581,9 @@ private void processAddedAttributes(UMLModelDiff umlModelDiffAll, Version curren if (currentAttribute == null || currentAttribute.isAdded()) { continue; } - Attribute rightAttribute = currentAttribute; + Attribute rightAttribute = getAttribute(rightModel, currentVersion, currentAttribute::equalIdentifierIgnoringVersion); + if (rightAttribute == null) + rightAttribute = currentAttribute; if (startAttributeChangeHistory.isAttributeAdded(umlModelDiffAll, rightAttribute.getUmlAttribute().getClassName(), currentVersion, parentVersion, rightAttribute::equalIdentifierIgnoringVersion, getAllClassesDiff(umlModelDiffAll))) { for (CodeElement key2 : programElementMap.keySet()) { if (key2 instanceof Comment) { @@ -681,6 +739,9 @@ private void processLocallyRefactoredAttributes(Map leftSideClasses = new HashSet<>(classRefactored); leftSideClasses.forEach(startInnerClassChangeHistory::addFirst); + if (leftSideClasses.size() == 1) { + startInnerClassChangeHistory.setCurrent(leftSideClasses.iterator().next()); + } for (CodeElement key : programElementMap.keySet()) { if (key instanceof Comment) { Comment startComment = (Comment)key; diff --git a/src/main/java/org/codetracker/FileTrackerWithLocalFilesImpl.java b/src/main/java/org/codetracker/FileTrackerWithLocalFilesImpl.java index 6fdd66e1c98..9d920985979 100644 --- a/src/main/java/org/codetracker/FileTrackerWithLocalFilesImpl.java +++ b/src/main/java/org/codetracker/FileTrackerWithLocalFilesImpl.java @@ -378,7 +378,8 @@ else if (leftSuperclass == null && rightSuperclass != null) { if (startClassChangeHistory.isClassAdded(umlModelDiffAll, currentVersion, parentVersion, rightClass::equalIdentifierIgnoringVersion)) { processAddedMethods(umlModelPairAll.getRight(), umlModelDiffAll, currentVersion, parentVersion); - processAddedAttributes(umlModelDiffAll, currentVersion, parentVersion); + processAddedAttributes(umlModelPairAll.getRight(), umlModelDiffAll, currentVersion, parentVersion); + processAddedInnerClasses(umlModelPairAll.getRight(), umlModelDiffAll, currentVersion, parentVersion, startClass); processAddedImportsAndClassComments(rightClass, parentVersion); break; } @@ -520,7 +521,62 @@ else if (key instanceof Comment) { } } - private void processAddedAttributes(UMLModelDiff umlModelDiffAll, Version currentVersion, Version parentVersion) { + private void processAddedInnerClasses(UMLModel rightModel, UMLModelDiff umlModelDiffAll, Version currentVersion, Version parentVersion, Class startClass) { + for (CodeElement key : programElementMap.keySet()) { + if (key instanceof Class && !key.equals(startClass)) { + Class startInnerClass = (Class)key; + ClassTrackerChangeHistory startInnerClassChangeHistory = (ClassTrackerChangeHistory) programElementMap.get(startInnerClass); + Class currentClass = startInnerClassChangeHistory.peek(); + if (currentClass == null) { + currentClass = startInnerClassChangeHistory.getCurrent(); + } + if (currentClass == null || currentClass.isAdded()) { + continue; + } + Class rightClass = getClass(rightModel, currentVersion, currentClass::equalIdentifierIgnoringVersion); + if (rightClass == null) { + continue; + } + startInnerClassChangeHistory.poll(); + if (startInnerClassChangeHistory.isClassAdded(umlModelDiffAll, currentVersion, parentVersion, rightClass::equalIdentifierIgnoringVersion)) { + for (CodeElement key2 : programElementMap.keySet()) { + if (key2 instanceof Comment) { + Comment startComment = (Comment)key2; + CommentTrackerChangeHistory startCommentChangeHistory = (CommentTrackerChangeHistory) programElementMap.get(startComment); + if ((startComment.getClazz().isPresent() && startComment.getClazz().get().equals(startInnerClass.getUmlClass())) || + (!startCommentChangeHistory.isEmpty() && startCommentChangeHistory.peek().getClazz().isPresent() && rightClass.getUmlClass().equals(startCommentChangeHistory.peek().getClazz().get()))) { + Comment currentComment = startCommentChangeHistory.poll(); + Comment rightComment = rightClass.findComment(currentComment::equalIdentifierIgnoringVersion); + if (rightComment != null) { + Comment commentBefore = Comment.of(rightComment.getComment(), rightComment.getClazz().get(), parentVersion); + startCommentChangeHistory.get().handleAdd(commentBefore, rightComment, "added with inner class"); + startCommentChangeHistory.add(commentBefore); + startCommentChangeHistory.get().connectRelatedNodes(); + } + } + } + else if (key2 instanceof Annotation) { + Annotation startAnnotation = (Annotation)key2; + AnnotationTrackerChangeHistory startAnnotationChangeHistory = (AnnotationTrackerChangeHistory) programElementMap.get(startAnnotation); + if ((startAnnotation.getClazz().isPresent() && startAnnotation.getClazz().get().equals(startInnerClass.getUmlClass())) || + (!startAnnotationChangeHistory.isEmpty() && startAnnotationChangeHistory.peek().getClazz().isPresent() && rightClass.getUmlClass().equals(startAnnotationChangeHistory.peek().getClazz().get()))) { + Annotation currentAnnotation = startAnnotationChangeHistory.poll(); + Annotation rightAnnotation = rightClass.findAnnotation(currentAnnotation::equalIdentifierIgnoringVersion); + if (rightAnnotation != null) { + Annotation annotationBefore = Annotation.of(rightAnnotation.getAnnotation(), rightAnnotation.getClazz().get(), parentVersion); + startAnnotationChangeHistory.get().handleAdd(annotationBefore, rightAnnotation, "added with inner class"); + startAnnotationChangeHistory.add(annotationBefore); + startAnnotationChangeHistory.get().connectRelatedNodes(); + } + } + } + } + } + } + } + } + + private void processAddedAttributes(UMLModel rightModel, UMLModelDiff umlModelDiffAll, Version currentVersion, Version parentVersion) { for (CodeElement key : programElementMap.keySet()) { if (key instanceof Attribute) { Attribute startAttribute = (Attribute)key; @@ -532,7 +588,9 @@ private void processAddedAttributes(UMLModelDiff umlModelDiffAll, Version curren if (currentAttribute == null || currentAttribute.isAdded()) { continue; } - Attribute rightAttribute = currentAttribute; + Attribute rightAttribute = getAttribute(rightModel, currentVersion, currentAttribute::equalIdentifierIgnoringVersion); + if (rightAttribute == null) + rightAttribute = currentAttribute; if (startAttributeChangeHistory.isAttributeAdded(umlModelDiffAll, rightAttribute.getUmlAttribute().getClassName(), currentVersion, parentVersion, rightAttribute::equalIdentifierIgnoringVersion, getAllClassesDiff(umlModelDiffAll))) { for (CodeElement key2 : programElementMap.keySet()) { if (key2 instanceof Comment) { @@ -688,6 +746,9 @@ private void processLocallyRefactoredAttributes(Map leftSideClasses = new HashSet<>(classRefactored); leftSideClasses.forEach(startInnerClassChangeHistory::addFirst); + if (leftSideClasses.size() == 1) { + startInnerClassChangeHistory.setCurrent(leftSideClasses.iterator().next()); + } for (CodeElement key : programElementMap.keySet()) { if (key instanceof Comment) { Comment startComment = (Comment)key; diff --git a/src/test/java/org/codetracker/blame/CodeTrackerBlameWithLocalFilesTest.java b/src/test/java/org/codetracker/blame/CodeTrackerBlameWithLocalFilesTest.java new file mode 100644 index 00000000000..e9312e04675 --- /dev/null +++ b/src/test/java/org/codetracker/blame/CodeTrackerBlameWithLocalFilesTest.java @@ -0,0 +1,48 @@ +package org.codetracker.blame; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.codetracker.FileTrackerWithLocalFilesImpl; +import org.codetracker.blame.util.BlameFormatter; +import org.codetracker.blame.util.TabularPrint; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.refactoringminer.astDiff.utils.URLHelper; + +public class CodeTrackerBlameWithLocalFilesTest { + + @ParameterizedTest + @CsvSource({ + "https://github.com/checkstyle/checkstyle/commit/119fd4fb33bef9f5c66fc950396669af842c21a3, src/main/java/com/puppycrawl/tools/checkstyle/Checker.java, /src/test/resources/blame/blameTestWithLocalRepo3.txt", + "https://github.com/javaparser/javaparser/commit/97555053af3025556efe1a168fd7943dac28a2a6, javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/Difference.java, /src/test/resources/blame/blameTestWithLocalRepo4.txt", + "https://github.com/javaparser/javaparser/commit/97555053af3025556efe1a168fd7943dac28a2a6, javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java, /src/test/resources/blame/blameTestWithLocalRepo5.txt", + "https://github.com/spring-projects/spring-framework/commit/b325c74216fd9564a36602158fa1269e2e832874, spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java, /src/test/resources/blame/blameTestWithLocalRepo6.txt", + "https://github.com/eclipse/jgit/commit/bd1a82502680b5de5bf86f6c4470185fd1602386, org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java, /src/test/resources/blame/blameTestUntilCommitZero.txt", + "https://github.com/JetBrains/intellij-community/commit/ecb1bb9d4d484ae63ee77f8ad45bdce154db9356, java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java, /src/test/resources/blame/blameTestUntilCommitZero2.txt", + "https://github.com/JetBrains/intellij-community/commit/ecb1bb9d4d484ae63ee77f8ad45bdce154db9356, java/compiler/impl/src/com/intellij/compiler/actions/CompileDirtyAction.java, /src/test/resources/blame/blameTestUntilCommitZero3.txt" + }) + public void testBlameWithLocalRepoUsingFileTracker(String url, String filePath, String testResultFileName) throws Exception { + String expectedFilePath = System.getProperty("user.dir") + testResultFileName; + String cloneURL = URLHelper.getRepo(url); + String commitId = URLHelper.getCommit(url); + FileTrackerWithLocalFilesImpl fileTracker = new FileTrackerWithLocalFilesImpl(cloneURL, commitId, filePath); + fileTracker.blame(); + BlameFormatter blameFormatter = new BlameFormatter(fileTracker.getLines()); + List results = blameFormatter.make(fileTracker.getBlameInfo()); + String actual = TabularPrint.make(results); + assertEqualWithFile(expectedFilePath, actual); + } + + private void assertEqualWithFile(String expectedResultPath, String actual) throws IOException { + String expected = IOUtils.toString( + new FileInputStream(expectedResultPath), + StandardCharsets.UTF_8 + ); + Assertions.assertEquals(expected, actual); + } +}