diff --git a/javaparser-core/src/main/java/com/github/javaparser/printer/concretesyntaxmodel/CsmAttribute.java b/javaparser-core/src/main/java/com/github/javaparser/printer/concretesyntaxmodel/CsmAttribute.java index b40e5e77e4..4e17281c11 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/printer/concretesyntaxmodel/CsmAttribute.java +++ b/javaparser-core/src/main/java/com/github/javaparser/printer/concretesyntaxmodel/CsmAttribute.java @@ -74,6 +74,8 @@ public int getTokenType(Node node, String text) { } throw new UnsupportedOperationException("getTokenType does not know how to handle value for " + node.getClass().getCanonicalName()); + case NAME: + return GeneratedJavaParserConstants.IDENTIFIER; default: throw new UnsupportedOperationException("getTokenType does not know how to handle property " + property + " with text: " + text); diff --git a/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/Difference.java b/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/Difference.java index be007ac5c8..3d7264d6d7 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/Difference.java +++ b/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/Difference.java @@ -5,8 +5,10 @@ import com.github.javaparser.ast.type.PrimitiveType; import com.github.javaparser.TokenTypes; import com.github.javaparser.printer.concretesyntaxmodel.*; +import com.github.javaparser.printer.lexicalpreservation.LexicalDifferenceCalculator.CsmChild; import java.util.*; +import java.util.stream.Collectors; import static com.github.javaparser.GeneratedJavaParserConstants.*; @@ -85,6 +87,49 @@ public boolean isAdded() { } } + private static class Reshuffled implements DifferenceElement { + CsmMix previousOrder; + CsmMix element; + + public Reshuffled(CsmMix previousOrder, CsmMix element) { + this.previousOrder = previousOrder; + this.element = element; + } + + @Override + public String toString() { + return "Reshuffled{" + element + ", previous="+ previousOrder+ '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Reshuffled that = (Reshuffled) o; + + if (!previousOrder.equals(that.previousOrder)) return false; + return element.equals(that.element); + } + + @Override + public int hashCode() { + int result = previousOrder.hashCode(); + result = 31 * result + element.hashCode(); + return result; + } + + @Override + public CsmMix getElement() { + return element; + } + + @Override + public boolean isAdded() { + return false; + } + } + private static class Kept implements DifferenceElement { CsmElement element; @@ -162,10 +207,10 @@ public boolean isAdded() { } private static boolean matching(CsmElement a, CsmElement b) { - if (a instanceof LexicalDifferenceCalculator.CsmChild) { - if (b instanceof LexicalDifferenceCalculator.CsmChild) { - LexicalDifferenceCalculator.CsmChild childA = (LexicalDifferenceCalculator.CsmChild) a; - LexicalDifferenceCalculator.CsmChild childB = (LexicalDifferenceCalculator.CsmChild) b; + if (a instanceof CsmChild) { + if (b instanceof CsmChild) { + CsmChild childA = (CsmChild) a; + CsmChild childB = (CsmChild) b; return childA.getChild().equals(childB.getChild()); } else if (b instanceof CsmToken) { return false; @@ -181,7 +226,7 @@ private static boolean matching(CsmElement a, CsmElement b) { CsmToken childA = (CsmToken)a; CsmToken childB = (CsmToken)b; return childA.getTokenType() == childB.getTokenType(); - } else if (b instanceof LexicalDifferenceCalculator.CsmChild) { + } else if (b instanceof CsmChild) { return false; } else if (b instanceof CsmIndent) { return false; @@ -202,10 +247,10 @@ private static boolean replacement(CsmElement a, CsmElement b) { if (a instanceof CsmIndent || b instanceof CsmIndent || a instanceof CsmUnindent || b instanceof CsmUnindent) { return false; } - if (a instanceof LexicalDifferenceCalculator.CsmChild) { - if (b instanceof LexicalDifferenceCalculator.CsmChild) { - LexicalDifferenceCalculator.CsmChild childA = (LexicalDifferenceCalculator.CsmChild) a; - LexicalDifferenceCalculator.CsmChild childB = (LexicalDifferenceCalculator.CsmChild) b; + if (a instanceof CsmChild) { + if (b instanceof CsmChild) { + CsmChild childA = (CsmChild) a; + CsmChild childB = (CsmChild) b; return childA.getChild().getClass().equals(childB.getClass()); } else if (b instanceof CsmToken) { return false; @@ -217,7 +262,7 @@ private static boolean replacement(CsmElement a, CsmElement b) { CsmToken childA = (CsmToken)a; CsmToken childB = (CsmToken)b; return childA.getTokenType() == childB.getTokenType(); - } else if (b instanceof LexicalDifferenceCalculator.CsmChild) { + } else if (b instanceof CsmChild) { return false; } } @@ -231,8 +276,8 @@ private static Map findChildrenPositions(LexicalDifferenceCalcula Map positions = new HashMap<>(); for (int i=0;i elementsInOriginalMix = new LinkedList<>(((CsmMix) nextOriginal).getElements()); - List elementsInAfterMix = new LinkedList<>(((CsmMix) nextAfter).getElements()); - - int[] indexOfCorrespondingAfterElementForOriginalElement = new int[elementsInOriginalMix.size()]; - int[] indexOfCorrespondingOriginalElementForAfterElement = new int[elementsInAfterMix.size()]; - - Arrays.fill(indexOfCorrespondingAfterElementForOriginalElement, -1); - Arrays.fill(indexOfCorrespondingOriginalElementForAfterElement, -1); - - for (int i=0;i= indexOfCorrespondingOriginalElementForAfterElement[k]) { - indexOfCorrespondingOriginalElementForAfterElement[z] += 1; - } - } - } - } - if (elementToPreceed == -1) { - elements.add(new Added(elementsInAfterMix.get(j))); - } - } - } - +// List elementsInOriginalMix = new LinkedList<>(((CsmMix) nextOriginal).getElements()); +// List elementsInAfterMix = new LinkedList<>(((CsmMix) nextAfter).getElements()); +// +// int[] indexOfCorrespondingAfterElementForOriginalElement = new int[elementsInOriginalMix.size()]; +// int[] indexOfCorrespondingOriginalElementForAfterElement = new int[elementsInAfterMix.size()]; +// +// Arrays.fill(indexOfCorrespondingAfterElementForOriginalElement, -1); +// Arrays.fill(indexOfCorrespondingOriginalElementForAfterElement, -1); +// +// for (int i=0;i= indexOfCorrespondingOriginalElementForAfterElement[k]) { +// indexOfCorrespondingOriginalElementForAfterElement[z] += 1; +// } +// } +// } +// } +// if (elementToPreceed == -1) { +// elements.add(new Added(elementsInAfterMix.get(j))); +// } +// } +// } +// +// originalIndex++; +// afterIndex++; + elements.add(new Reshuffled((CsmMix)nextOriginal, (CsmMix)nextAfter)); originalIndex++; afterIndex++; } else if (matching(nextOriginal, nextAfter)) { @@ -397,8 +445,8 @@ && matching(elementsInOriginalMix.get(i), elementsInAfterMix.get(j))) { } private TextElement toTextElement(LexicalPreservingPrinter lpp, CsmElement csmElement) { - if (csmElement instanceof LexicalDifferenceCalculator.CsmChild) { - return new ChildTextElement(lpp, ((LexicalDifferenceCalculator.CsmChild) csmElement).getChild()); + if (csmElement instanceof CsmChild) { + return new ChildTextElement(lpp, ((CsmChild) csmElement).getChild()); } else if (csmElement instanceof CsmToken) { return new TokenTextElement(((CsmToken) csmElement).getTokenType(), ((CsmToken) csmElement).getContent(null)); } else { @@ -587,17 +635,17 @@ void apply(NodeText nodeText, Node node) { diffIndex++; } else if (diffEl instanceof Kept) { Kept kept = (Kept)diffEl; - if ((kept.element instanceof LexicalDifferenceCalculator.CsmChild) && nodeTextEl.isComment()) { + if ((kept.element instanceof CsmChild) && nodeTextEl.isComment()) { nodeTextIndex++; - } else if ((kept.element instanceof LexicalDifferenceCalculator.CsmChild) && nodeTextEl instanceof ChildTextElement) { + } else if ((kept.element instanceof CsmChild) && nodeTextEl instanceof ChildTextElement) { diffIndex++; nodeTextIndex++; - } else if ((kept.element instanceof LexicalDifferenceCalculator.CsmChild) && nodeTextEl instanceof TokenTextElement) { + } else if ((kept.element instanceof CsmChild) && nodeTextEl instanceof TokenTextElement) { if (((TokenTextElement) nodeTextEl).isWhiteSpaceOrComment()) { nodeTextIndex++; } else { - if (kept.element instanceof LexicalDifferenceCalculator.CsmChild) { - LexicalDifferenceCalculator.CsmChild keptChild = (LexicalDifferenceCalculator.CsmChild)kept.element; + if (kept.element instanceof CsmChild) { + CsmChild keptChild = (CsmChild)kept.element; if (keptChild.getChild() instanceof PrimitiveType) { nodeTextIndex++; diffIndex++; @@ -637,7 +685,7 @@ void apply(NodeText nodeText, Node node) { } } else if (diffEl instanceof Removed) { Removed removed = (Removed)diffEl; - if ((removed.element instanceof LexicalDifferenceCalculator.CsmChild) && nodeTextEl instanceof ChildTextElement) { + if ((removed.element instanceof CsmChild) && nodeTextEl instanceof ChildTextElement) { nodeText.removeElement(nodeTextIndex); if (nodeTextIndex < nodeText.getElements().size() && nodeText.getElements().get(nodeTextIndex).isNewline()) { nodeTextIndex = considerCleaningTheLine(nodeText, nodeTextIndex); @@ -654,8 +702,8 @@ void apply(NodeText nodeText, Node node) { } else if (nodeTextEl instanceof TokenTextElement && nodeTextEl.isWhiteSpaceOrComment()) { nodeTextIndex++; - } else if (removed.element instanceof LexicalDifferenceCalculator.CsmChild - && ((LexicalDifferenceCalculator.CsmChild)removed.element).getChild() instanceof PrimitiveType) { + } else if (removed.element instanceof CsmChild + && ((CsmChild)removed.element).getChild() instanceof PrimitiveType) { if (isPrimitiveType(nodeTextEl)) { nodeText.removeElement(nodeTextIndex); diffIndex++; @@ -669,6 +717,129 @@ void apply(NodeText nodeText, Node node) { } else { throw new UnsupportedOperationException("removed " + removed.element + " vs " + nodeTextEl); } + } else if (diffEl instanceof Reshuffled) { + + // First, let's see how many tokens we need to attribute to the previous version of the of the CsmMix + Reshuffled reshuffled = (Reshuffled)diffEl; + CsmMix elementsFromPreviousOrder = reshuffled.previousOrder; + CsmMix elementsFromNextOrder = reshuffled.element; + + // This contains indexes from elementsFromNextOrder to indexes from elementsFromPreviousOrder + Map correspondanceBetweenNextOrderAndPreviousOrder = new HashMap<>(); + for (int ni=0;ni usedIndexes = new HashSet<>(); + List nodeTextIndexOfPreviousElements = elementsFromPreviousOrder.getElements().stream() + .map(it -> findIndexOfCorrespondingNodeTextElement(it, nodeText, startNodeTextIndex, usedIndexes, node)) + .collect(Collectors.toList()); + Map nodeTextIndexToPreviousCSMIndex = new HashMap<>(); + for (int i=0;i elementsToBeAddedAtTheEnd = new LinkedList<>(); + Map> elementsToAddBeforeGivenOriginalCSMElement = new HashMap<>(); + for (int ni=0;ni()); + } + elementsToAddBeforeGivenOriginalCSMElement.get(originalCsmIndex).add(elementsFromNextOrder.getElements().get(ni)); + } + } + // it does not preceed anything, so it goes at the end + if (originalCsmIndex == -1) { + elementsToBeAddedAtTheEnd.add(elementsFromNextOrder.getElements().get(ni)); + } + } + } + + + + //Map> elementsToAddBeforeGivenNodeTextIndex = new HashMap<>(); + //List elementsToBeAddedAtTheEnd = new LinkedList<>(); + //Set nodeTextElementsToKeep = new HashSet<>(); + + // Let's figure out which ones of the existing tokens are going to be removed + + // Now I should translate all of this to a sequence of additions and deletions + // and then just add these differences to the diff to be treated normally + +// // Now let's understand how the new elements are positioned w.r.t. to the kept elements +// if (nodeTextIndexOfPreviousElements.isEmpty()) { +// // They are all just following the sequence in the new mix +// for (CsmElement csmElement : reshuffled.getElement().getElements()) { +// nodeText.addElement(nodeTextIndex++, toTextElement()); +// } +// } else { +// throw new UnsupportedOperationException(); +// } + + // We go over the original node text elements, in the order they appear in the NodeText. + // Considering an original node text element (ONE) + // * we verify if it corresponds to a CSM element. If it does not we just move on, otherwise + // we find the correspond OCE (Original CSM Element) + // * we first add new elements that are marked to be added before OCE + // * if OCE is marked to be present also in the "after" CSM we add a kept element, + // otherwise we add a removed element + + this.getElements().remove(diffIndex); + int diffElIterator = diffIndex; + if (lastNodeTextIndex != -1) { + for (int ntIndex = startNodeTextIndex; ntIndex<=lastNodeTextIndex; ntIndex++) { + + if (nodeTextIndexToPreviousCSMIndex.containsKey(ntIndex)) { + int indexOfOriginalCSMElement = nodeTextIndexToPreviousCSMIndex.get(ntIndex); + if (elementsToAddBeforeGivenOriginalCSMElement.containsKey(indexOfOriginalCSMElement)) { + for (CsmElement elementToAdd : elementsToAddBeforeGivenOriginalCSMElement.get(indexOfOriginalCSMElement)) { + elements.add(diffElIterator++, new Added(elementToAdd)); + } + } + + CsmElement originalCSMElement = elementsFromPreviousOrder.getElements().get(indexOfOriginalCSMElement); + boolean toBeKept = correspondanceBetweenNextOrderAndPreviousOrder.containsValue(indexOfOriginalCSMElement); + if (toBeKept) { + elements.add(diffElIterator++, new Kept(originalCSMElement)); + } else { + elements.add(diffElIterator++, new Removed(originalCSMElement)); + } + } else { + // simple node text element, without associated csm element, just keep ignore it + } + + } + } + + // Finally we look for the remaining new elements that were not yet added and + // add all of them + for (CsmElement elementToAdd : elementsToBeAddedAtTheEnd) { + elements.add(diffElIterator++, new Added(elementToAdd)); + } } else { throw new UnsupportedOperationException("" + diffEl + " vs " + nodeTextEl); } @@ -676,6 +847,36 @@ void apply(NodeText nodeText, Node node) { } while (diffIndex < this.elements.size() || nodeTextIndex < nodeText.getElements().size()); } + private int findIndexOfCorrespondingNodeTextElement(CsmElement csmElement, NodeText nodeText, int startIndex, Set usedIndexes, Node node) { + for (int i=startIndex;i indentation, NodeText nodeText, int nodeTextIndex, boolean followedByUnindent) { List indentationAdj = processIndentation(indentation, nodeText.getElements().subList(0, nodeTextIndex - 1)); if (nodeTextIndex < nodeText.getElements().size() && nodeText.getElements().get(nodeTextIndex).isToken(RBRACE)) { diff --git a/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinterTest.java b/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinterTest.java index 80efee7bba..fb24727345 100644 --- a/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinterTest.java +++ b/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinterTest.java @@ -794,8 +794,11 @@ public void moveOverrideAnnotations() { if (member instanceof MethodDeclaration) { MethodDeclaration methodDeclaration = (MethodDeclaration) member; if (methodDeclaration.getAnnotationByName("Override").isPresent()) { - methodDeclaration.getAnnotations().stream() - .forEach(anno -> anno.remove()); + + while (methodDeclaration.getAnnotations().isNonEmpty()) { + AnnotationExpr annotationExpr = methodDeclaration.getAnnotations().get(0); + annotationExpr.remove(); + } methodDeclaration.addMarkerAnnotation("Override"); }