JavaParser Source Viewer

Home|JavaParser/com/github/javaparser/printer/lexicalpreservation/Difference.java
1/*
2 * Copyright (C) 2007-2010 JĂșlio Vilmar Gesser.
3 * Copyright (C) 2011, 2013-2020 The JavaParser Team.
4 *
5 * This file is part of JavaParser.
6 *
7 * JavaParser can be used either under the terms of
8 * a) the GNU Lesser General Public License as published by
9 *     the Free Software Foundation, either version 3 of the License, or
10 *     (at your option) any later version.
11 * b) the terms of the Apache License
12 *
13 * You should have received a copy of both licenses in LICENCE.LGPL and
14 * LICENCE.APACHE. Please refer to those files for details.
15 *
16 * JavaParser is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU Lesser General Public License for more details.
20 */
21
22package com.github.javaparser.printer.lexicalpreservation;
23
24import static com.github.javaparser.GeneratedJavaParserConstants.LBRACE;
25import static com.github.javaparser.GeneratedJavaParserConstants.RBRACE;
26import static com.github.javaparser.GeneratedJavaParserConstants.SPACE;
27
28import java.util.ArrayList;
29import java.util.Comparator;
30import java.util.EnumMap;
31import java.util.HashMap;
32import java.util.LinkedList;
33import java.util.List;
34import java.util.ListIterator;
35import java.util.Map;
36import java.util.Optional;
37
38import com.github.javaparser.GeneratedJavaParserConstants;
39import com.github.javaparser.JavaToken;
40import com.github.javaparser.JavaToken.Kind;
41import com.github.javaparser.TokenTypes;
42import com.github.javaparser.ast.Node;
43import com.github.javaparser.ast.NodeList;
44import com.github.javaparser.ast.comments.Comment;
45import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments;
46import com.github.javaparser.ast.type.Type;
47import com.github.javaparser.printer.concretesyntaxmodel.CsmElement;
48import com.github.javaparser.printer.concretesyntaxmodel.CsmIndent;
49import com.github.javaparser.printer.concretesyntaxmodel.CsmMix;
50import com.github.javaparser.printer.concretesyntaxmodel.CsmToken;
51import com.github.javaparser.printer.concretesyntaxmodel.CsmUnindent;
52import com.github.javaparser.printer.lexicalpreservation.LexicalDifferenceCalculator.CsmChild;
53
54/**
55 * A Difference should give me a sequence of elements I should find (to indicate the context) followed by a list of elements
56 * to remove or to add and follow by another sequence of elements.
57 *
58 * I should later be able to apply such difference to a nodeText.
59 */
60public class Difference {
61
62    public static final int STANDARD_INDENTATION_SIZE = 4;
63
64    private final NodeText nodeText;
65    private final Node node;
66
67    private final List<DifferenceElementdiffElements;
68    private final List<TextElementoriginalElements;
69    private int originalIndex = 0;
70    private int diffIndex = 0;
71
72    private final List<TokenTextElementindentation;
73    private boolean addedIndentation = false;
74
75    Difference(List<DifferenceElementdiffElementsNodeText nodeTextNode node) {
76        if (nodeText == null) {
77            throw new NullPointerException("nodeText can not be null");
78        }
79
80        this.nodeText = nodeText;
81        this.node = node;
82        this.diffElements = diffElements;
83        this.originalElements = nodeText.getElements();
84
85        this.indentation = LexicalPreservingPrinter.findIndentation(node);
86    }
87
88    private List<TextElementprocessIndentation(List<TokenTextElementindentationList<TextElementprevElements) {
89        List<TextElementres = new LinkedList<>(indentation);
90        boolean afterNl = false;
91        for (TextElement e : prevElements) {
92            if (e.isNewline()) {
93                res.clear();
94                afterNl = true;
95            } else {
96                if (afterNl && e instanceof TokenTextElement && TokenTypes.isWhitespace(((TokenTextElement)e).getTokenKind())) {
97                    res.add(e);
98                } else {
99                    afterNl = false;
100                }
101            }
102        }
103        return res;
104    }
105
106    private List<TextElementindentationBlock() {
107        List<TextElementres = new LinkedList<>();
108        res.add(new TokenTextElement(SPACE));
109        res.add(new TokenTextElement(SPACE));
110        res.add(new TokenTextElement(SPACE));
111        res.add(new TokenTextElement(SPACE));
112        return res;
113    }
114
115    private boolean isAfterLBrace(NodeText nodeTextint nodeTextIndex) {
116        if (nodeTextIndex > 0 && nodeText.getElements().get(nodeTextIndex - 1).isToken(LBRACE)) {
117            return true;
118        }
119        if (nodeTextIndex > 0 && nodeText.getElements().get(nodeTextIndex - 1).isSpaceOrTab()) {
120            return isAfterLBrace(nodeTextnodeTextIndex - 1);
121        }
122        return false;
123    }
124
125    /**
126     * If we are at the beginning of a line, with just spaces or tabs before us we should force the space to be
127     * the same as the indentation.
128     */
129    private int considerEnforcingIndentation(NodeText nodeTextint nodeTextIndex) {
130        boolean hasOnlyWsBefore = true;
131        for (int i = nodeTextIndexi >= 0 && hasOnlyWsBefore && i < nodeText.getElements().size(); i--) {
132            if (nodeText.getElements().get(i).isNewline()) {
133                break;
134            }
135            if (!nodeText.getElements().get(i).isSpaceOrTab()) {
136                hasOnlyWsBefore = false;
137            }
138        }
139        int res = nodeTextIndex;
140        if (hasOnlyWsBefore) {
141            for (int i = nodeTextIndexi >= 0 && i < nodeText.getElements().size(); i--) {
142                if (nodeText.getElements().get(i).isNewline()) {
143                    break;
144                }
145                nodeText.removeElement(i);
146                res = i;
147            }
148        }
149        if (res < 0) {
150            throw new IllegalStateException();
151        }
152        return res;
153    }
154
155    /**
156     * Node that we have calculate the Difference we can apply to a concrete NodeText, modifying it according
157     * to the difference (adding and removing the elements provided).
158     */
159    void apply() {
160        extractReshuffledDiffElements(diffElements);
161        Map<RemovedRemovedGroupremovedGroups = combineRemovedElementsToRemovedGroups();
162
163        do {
164            boolean isLeftOverDiffElement = applyLeftOverDiffElements();
165            boolean isLeftOverOriginalElement = applyLeftOverOriginalElements();
166
167            if (!isLeftOverDiffElement && !isLeftOverOriginalElement){
168                DifferenceElement diffElement = diffElements.get(diffIndex);
169
170                if (diffElement instanceof Added) {
171                    applyAddedDiffElement((AddeddiffElement);
172                } else {
173                    TextElement originalElement = originalElements.get(originalIndex);
174                    boolean originalElementIsChild = originalElement instanceof ChildTextElement;
175                    boolean originalElementIsToken = originalElement instanceof TokenTextElement;
176
177                    if (diffElement instanceof Kept) {
178                        applyKeptDiffElement((KeptdiffElementoriginalElementoriginalElementIsChildoriginalElementIsToken);
179                    } else if (diffElement instanceof Removed) {
180                        Removed removed = (RemoveddiffElement;
181                        applyRemovedDiffElement(removedGroups.get(removed), removedoriginalElementoriginalElementIsChildoriginalElementIsToken);
182                    } else {
183                        throw new UnsupportedOperationException("" + diffElement + " vs " + originalElement);
184                    }
185                }
186            }
187        } while (diffIndex < diffElements.size() || originalIndex < originalElements.size());
188    }
189
190    private boolean applyLeftOverOriginalElements() {
191        boolean isLeftOverElement = false;
192        if (diffIndex >= diffElements.size() && originalIndex < originalElements.size()) {
193            TextElement originalElement = originalElements.get(originalIndex);
194
195            if (originalElement.isWhiteSpaceOrComment()) {
196                originalIndex++;
197            } else {
198                throw new UnsupportedOperationException("NodeText: " + nodeText + ". Difference: "
199                        + this + " " + originalElement);
200            }
201
202            isLeftOverElement = true;
203        }
204        return isLeftOverElement;
205    }
206
207    private boolean applyLeftOverDiffElements() {
208        boolean isLeftOverElement = false;
209        if (diffIndex < diffElements.size() && originalIndex >= originalElements.size()) {
210            DifferenceElement diffElement = diffElements.get(diffIndex);
211            if (diffElement instanceof Kept) {
212                Kept kept = (KeptdiffElement;
213
214                if (kept.isWhiteSpaceOrComment() || kept.isIndent() || kept.isUnindent()) {
215                    diffIndex++;
216                } else {
217                    throw new IllegalStateException("Cannot keep element because we reached the end of nodetext: "
218                            + nodeText + ". Difference: " + this);
219                }
220            } else if (diffElement instanceof Added) {
221                Added addedElement = (AddeddiffElement;
222
223                nodeText.addElement(originalIndexaddedElement.toTextElement());
224                originalIndex++;
225                diffIndex++;
226            } else {
227                throw new UnsupportedOperationException(diffElement.getClass().getSimpleName());
228            }
229
230            isLeftOverElement = true;
231        }
232
233        return isLeftOverElement;
234    }
235
236    private void extractReshuffledDiffElements(List<DifferenceElementdiffElements) {
237        for (int index = 0index < diffElements.size(); index++) {
238            DifferenceElement diffElement = diffElements.get(index);
239            if (diffElement instanceof Reshuffled) {
240                Reshuffled reshuffled = (ReshuffleddiffElement;
241
242                // First, let's see how many tokens we need to attribute to the previous version of the of the CsmMix
243                CsmMix elementsFromPreviousOrder = reshuffled.getPreviousOrder();
244                CsmMix elementsFromNextOrder = reshuffled.getNextOrder();
245
246                // This contains indexes from elementsFromNextOrder to indexes from elementsFromPreviousOrder
247                Map<IntegerIntegercorrespondanceBetweenNextOrderAndPreviousOrder = getCorrespondanceBetweenNextOrderAndPreviousOrder(elementsFromPreviousOrderelementsFromNextOrder);
248
249                // We now find out which Node Text elements corresponds to the elements in the original CSM
250                List<IntegernodeTextIndexOfPreviousElements = findIndexOfCorrespondingNodeTextElement(elementsFromPreviousOrder.getElements(), nodeTextoriginalIndexnode);
251
252                Map<IntegerIntegernodeTextIndexToPreviousCSMIndex = new HashMap<>();
253                for (int i = 0i < nodeTextIndexOfPreviousElements.size(); i++) {
254                    int value = nodeTextIndexOfPreviousElements.get(i);
255                    if (value != -1) {
256                        nodeTextIndexToPreviousCSMIndex.put(valuei);
257                    }
258                }
259                int lastNodeTextIndex = nodeTextIndexOfPreviousElements.stream().max(Integer::compareTo).orElse(-1);
260
261                // Elements to be added at the end
262                List<CsmElementelementsToBeAddedAtTheEnd = new LinkedList<>();
263                List<CsmElementnextOrderElements = elementsFromNextOrder.getElements();
264
265                Map<IntegerList<CsmElement>> elementsToAddBeforeGivenOriginalCSMElement = new HashMap<>();
266                for (int ni = 0ni < nextOrderElements.size(); ni++) {
267                    // If it has a mapping, then it is kept
268                    if (!correspondanceBetweenNextOrderAndPreviousOrder.containsKey(ni)) {
269                        // Ok, it is something new. Where to put it? Let's see what is the first following
270                        // element that has a mapping
271                        int originalCsmIndex = -1;
272                        for (int nj = ni + 1nj < nextOrderElements.size() && originalCsmIndex == -1nj++) {
273                            if (correspondanceBetweenNextOrderAndPreviousOrder.containsKey(nj)) {
274                                originalCsmIndex = correspondanceBetweenNextOrderAndPreviousOrder.get(nj);
275                                if (!elementsToAddBeforeGivenOriginalCSMElement.containsKey(originalCsmIndex)) {
276                                    elementsToAddBeforeGivenOriginalCSMElement.put(originalCsmIndex, new LinkedList<>());
277                                }
278                                elementsToAddBeforeGivenOriginalCSMElement.get(originalCsmIndex).add(nextOrderElements.get(ni));
279                            }
280                        }
281                        // it does not preceed anything, so it goes at the end
282                        if (originalCsmIndex == -1) {
283                            elementsToBeAddedAtTheEnd.add(nextOrderElements.get(ni));
284                        }
285                    }
286                }
287
288                // We go over the original node text elements, in the order they appear in the NodeText.
289                // Considering an original node text element (ONE)
290                // * we verify if it corresponds to a CSM element. If it does not we just move on, otherwise
291                //   we find the correspond OCE (Original CSM Element)
292                // * we first add new elements that are marked to be added before OCE
293                // * if OCE is marked to be present also in the "after" CSM we add a kept element,
294                //   otherwise we add a removed element
295
296                // Remove the whole Reshuffled element
297                diffElements.remove(index);
298
299                int diffElIterator = index;
300                if (lastNodeTextIndex != -1) {
301                    for (int ntIndex = originalIndexntIndex <= lastNodeTextIndexntIndex++) {
302
303                        if (nodeTextIndexToPreviousCSMIndex.containsKey(ntIndex)) {
304                            int indexOfOriginalCSMElement = nodeTextIndexToPreviousCSMIndex.get(ntIndex);
305                            if (elementsToAddBeforeGivenOriginalCSMElement.containsKey(indexOfOriginalCSMElement)) {
306                                for (CsmElement elementToAdd : elementsToAddBeforeGivenOriginalCSMElement.get(indexOfOriginalCSMElement)) {
307                                    diffElements.add(diffElIterator++, new Added(elementToAdd));
308                                }
309                            }
310
311                            CsmElement originalCSMElement = elementsFromPreviousOrder.getElements().get(indexOfOriginalCSMElement);
312                            boolean toBeKept = correspondanceBetweenNextOrderAndPreviousOrder.containsValue(indexOfOriginalCSMElement);
313                            if (toBeKept) {
314                                diffElements.add(diffElIterator++, new Kept(originalCSMElement));
315                            } else {
316                                diffElements.add(diffElIterator++, new Removed(originalCSMElement));
317                            }
318                        }
319                        // else we have a simple node text element, without associated csm element, just keep ignore it
320                    }
321                }
322
323                // Finally we look for the remaining new elements that were not yet added and
324                // add all of them
325                for (CsmElement elementToAdd : elementsToBeAddedAtTheEnd) {
326                    diffElements.add(diffElIterator++, new Added(elementToAdd));
327                }
328            }
329        }
330    }
331
332    /**
333     * Maps all Removed elements as keys to their corresponding RemovedGroup.
334     * A RemovedGroup contains all consecutive Removed elements.
335     * <br>
336     * Example:
337     * <pre>
338     * Elements: Kept|Removed1|Removed2|Kept|Removed3|Added|Removed4
339     * Groups:        <----Group1---->       Group2         Group3
340     * Keys:          Removed1+Removed2      Removed3       Removed4
341     * </pre>
342     *
343     * @return Map with all Removed elements as keys to their corresponding RemovedGroup
344     */
345    private Map<RemovedRemovedGroupcombineRemovedElementsToRemovedGroups() {
346        Map<IntegerList<Removed>> removedElementsMap = groupConsecutiveRemovedElements();
347
348        List<RemovedGroupremovedGroups = new ArrayList<>();
349        for (Map.Entry<IntegerList<Removed>> entry : removedElementsMap.entrySet()) {
350            removedGroups.add(RemovedGroup.of(entry.getKey(), entry.getValue()));
351        }
352
353        Map<RemovedRemovedGroupmap = new HashMap<>();
354        for (RemovedGroup removedGroup : removedGroups){
355            for (Removed index : removedGroup) {
356                map.put(indexremovedGroup);
357            }
358        }
359
360        return map;
361    }
362
363    private Map<IntegerList<Removed>> groupConsecutiveRemovedElements() {
364        Map<IntegerList<Removed>> removedElementsMap = new HashMap<>();
365
366        Integer firstElement = null;
367        for (int i = 0i < diffElements.size(); i++) {
368            DifferenceElement diffElement = diffElements.get(i);
369            if (diffElement instanceof Removed) {
370                if (firstElement == null) {
371                    firstElement = i;
372                }
373
374                removedElementsMap.computeIfAbsent(firstElementkey -> new ArrayList<>())
375                        .add((RemoveddiffElement);
376            } else {
377                firstElement = null;
378            }
379        }
380        return removedElementsMap;
381    }
382
383    private void applyRemovedDiffElement(RemovedGroup removedGroupRemoved removedTextElement originalElementboolean originalElementIsChildboolean originalElementIsToken) {
384        if (removed.isChild() && originalElementIsChild) {
385            ChildTextElement originalElementChild = (ChildTextElementoriginalElement;
386            if (originalElementChild.isComment()) {
387                // We expected to remove a proper node but we found a comment in between.
388                // If the comment is associated to the node we want to remove we remove it as well, otherwise we keep it
389                Comment comment = (CommentoriginalElementChild.getChild();
390                if (!comment.isOrphan() && comment.getCommentedNode().isPresent() && comment.getCommentedNode().get().equals(removed.getChild())) {
391                    nodeText.removeElement(originalIndex);
392                } else {
393                    originalIndex++;
394                }
395            } else {
396                nodeText.removeElement(originalIndex);
397
398                if ((diffIndex + 1 >= diffElements.size() || !(diffElements.get(diffIndex + 1) instanceof Added))
399                        && !removedGroup.isACompleteLine()) {
400                    originalIndex = considerEnforcingIndentation(nodeTextoriginalIndex);
401                }
402                // If in front we have one space and before also we had space let's drop one space
403                if (originalElements.size() > originalIndex && originalIndex > 0) {
404                    if (originalElements.get(originalIndex).isWhiteSpace()
405                            && originalElements.get(originalIndex - 1).isWhiteSpace()) {
406                        // However we do not want to do that when we are about to adding or removing elements
407                        if ((diffIndex + 1) == diffElements.size() || (diffElements.get(diffIndex + 1) instanceof Kept)) {
408                            originalElements.remove(originalIndex--);
409                        }
410                    }
411                }
412
413                diffIndex++;
414            }
415        } else if (removed.isToken() && originalElementIsToken &&
416                (removed.getTokenType() == ((TokenTextElementoriginalElement).getTokenKind()
417                        // handle EOLs separately as their token kind might not be equal. This is because the 'removed'
418                        // element always has the current operating system's EOL as type
419                        || (((TokenTextElementoriginalElement).getToken().getCategory().isEndOfLine()
420                                && removed.isNewLine()))) {
421            nodeText.removeElement(originalIndex);
422            diffIndex++;
423        } else if (originalElementIsToken && originalElement.isWhiteSpaceOrComment()) {
424            originalIndex++;
425        } else if (originalElement.isLiteral()) {
426            nodeText.removeElement(originalIndex);
427            diffIndex++;
428        } else if (removed.isPrimitiveType()) {
429            if (originalElement.isPrimitive()) {
430                nodeText.removeElement(originalIndex);
431                diffIndex++;
432            } else {
433                throw new UnsupportedOperationException("removed " + removed.getElement() + " vs " + originalElement);
434            }
435        } else if (removed.isWhiteSpace() || removed.getElement() instanceof CsmIndent || removed.getElement() instanceof CsmUnindent) {
436            diffIndex++;
437        } else if (originalElement.isWhiteSpace()) {
438            originalIndex++;
439        } else {
440            throw new UnsupportedOperationException("removed " + removed.getElement() + " vs " + originalElement);
441        }
442
443        cleanTheLineOfLeftOverSpace(removedGroupremoved);
444    }
445
446    /**
447     * Cleans the line of left over space if there is unnecessary indentation and the element will not be replaced
448     */
449    private void cleanTheLineOfLeftOverSpace(RemovedGroup removedGroupRemoved removed) {
450        if (originalIndex >= originalElements.size()) {
451            // if all elements were already processed there is nothing to do
452            return;
453        }
454
455        if (!removedGroup.isProcessed()
456                && removedGroup.getLastElement() == removed
457                && removedGroup.isACompleteLine()) {
458            Integer lastElementIndex = removedGroup.getLastElementIndex();
459            Optional<Integerindentation = removedGroup.getIndentation();
460
461            if (indentation.isPresent() && !isReplaced(lastElementIndex)) {
462                for (int i = 0i < indentation.get(); i++) {
463                    if (originalElements.get(originalIndex).isSpaceOrTab()) {
464                        // If the current element is a space, remove it
465                        nodeText.removeElement(originalIndex);
466                    } else if (originalIndex >= 1 && originalElements.get(originalIndex - 1).isSpaceOrTab()) {
467                        // If the current element is not a space itself we remove the space in front of it
468                        nodeText.removeElement(originalIndex - 1);
469                        originalIndex--;
470                    }
471                }
472            }
473
474            // Mark RemovedGroup as processed
475            removedGroup.processed();
476        }
477    }
478
479    // note: 
480    // increment originalIndex if we want to keep the original element
481    // increment diffIndex if we don't want to skip the diff element 
482    private void applyKeptDiffElement(Kept keptTextElement originalElementboolean originalElementIsChildboolean originalElementIsToken) {
483        if (originalElement.isComment()) {
484            originalIndex++;
485        } else if (kept.isChild() && ((CsmChild)kept.getElement()).getChild() instanceof Comment ) {
486            diffIndex++;
487        } else if (kept.isChild() && originalElementIsChild) {
488            diffIndex++;
489            originalIndex++;
490        } else if (kept.isChild() && originalElementIsToken) {
491            if (originalElement.isWhiteSpaceOrComment()) {
492                originalIndex++;
493            } else if (originalElement.isIdentifier() && isNodeWithTypeArguments(kept)) {
494                diffIndex++;
495                // skip all token related to node with type argument declaration
496                // for example:
497                // List i : in this case originalElement is "List" and the next token is space. There is nothing to skip. in the originalElements list.
498                // List<String> i : in this case originalElement is "List" and the next token is
499                // "<" so we have to skip all the tokens which are used in the typed argument declaration [<][String][>](3 tokens) in the originalElements list.
500                // List<List<String>> i : in this case originalElement is "List" and the next
501                // token is "<" so we have to skip all the tokens which are used in the typed arguments declaration [<][List][<][String][>][>](6 tokens) in the originalElements list.
502                int step = getIndexToNextTokenElement((TokenTextElementoriginalElement0);
503                originalIndex += step;
504                originalIndex++;
505            } else if (originalElement.isIdentifier()) {
506                originalIndex++;
507                diffIndex++;
508            } else {
509                if (kept.isPrimitiveType()) {
510                    originalIndex++;
511                    diffIndex++;
512                } else {
513                    throw new UnsupportedOperationException("kept " + kept.getElement() + " vs " + originalElement);
514                }
515            }
516        } else if (kept.isToken() && originalElementIsToken) {
517            TokenTextElement originalTextToken = (TokenTextElementoriginalElement;
518
519            if (kept.getTokenType() == originalTextToken.getTokenKind()) {
520                originalIndex++;
521                diffIndex++;
522            } else if (kept.isNewLine() && originalTextToken.isNewline()) {
523                originalIndex++;
524                diffIndex++;
525            } else if (kept.isNewLine() && originalTextToken.isSpaceOrTab()) {
526                originalIndex++;
527                diffIndex++;
528             // case where originalTextToken is a separator like ";" and
529             // kept is not a new line or whitespace for example "}"
530             // see issue 2351
531            }  else if (!kept.isNewLine() && originalTextToken.isSeparator()) {
532                originalIndex++;
533            } else if (kept.isWhiteSpaceOrComment()) {
534                diffIndex++;
535            } else if (originalTextToken.isWhiteSpaceOrComment()) {
536                originalIndex++;
537            } else {
538                throw new UnsupportedOperationException("Csm token " + kept.getElement() + " NodeText TOKEN " + originalTextToken);
539            }
540        } else if (kept.isToken() && originalElementIsChild) {
541            diffIndex++;
542        } else if (kept.isWhiteSpace()) {
543            diffIndex++;
544        } else if (kept.isIndent()) {
545            diffIndex++;
546        } else if (kept.isUnindent()) {
547            // Nothing to do, beside considering indentation
548            // However we want to consider the case in which the indentation was not applied, like when we have
549            // just a left brace followed by space
550
551            diffIndex++;
552            if (!openBraceWasOnSameLine()) {
553                for (int i = 0i < STANDARD_INDENTATION_SIZE && originalIndex >= 1 && nodeText.getTextElement(originalIndex - 1).isSpaceOrTab(); i++) {
554                    nodeText.removeElement(--originalIndex);
555                }
556            }
557        } else {
558            throw new UnsupportedOperationException("kept " + kept.getElement() + " vs " + originalElement);
559        }
560    }
561
562    /*
563     * Returns true if the DifferenceElement is a CsmChild with type arguments
564     */
565    private boolean isNodeWithTypeArguments(DifferenceElement element) {
566        CsmElement csmElem = element.getElement();
567        if (!CsmChild.class.isAssignableFrom(csmElem.getClass()))
568            return false;
569        CsmChild child = (CsmChildcsmElem;
570        if (!NodeWithTypeArguments.class.isAssignableFrom(child.getChild().getClass()))
571            return false;
572        Optional<NodeList<Type>> typeArgs = ((NodeWithTypeArgumentschild.getChild()).getTypeArguments();
573        return typeArgs.isPresent() && typeArgs.get().size() > 0;
574    }
575
576    /*
577     * Returns the number of tokens to skip in originalElements list to synchronize it with the DiffElements list
578     * This is due to the fact that types are considered as token in the originalElements list.
579     * For example,
580     * List<String> is represented by 4 tokens ([List][<][String][>]) while it's a CsmChild element in the DiffElements list
581     * So in this case, getIndexToNextTokenElement(..) on the [List] token returns 3 because we have to skip 3 tokens ([<][String][>]) to synchronize
582     * DiffElements list and originalElements list 
583     * The end of recursivity is reached when there is no next token or if the nested diamond operators are totally managed, to take into account this type of declaration
584     * List <List<String>> l
585     * Be careful, this method must be call only if diamond operator could be found in the sequence
586     * 
587     * @Param TokenTextElement the token currently analyzed 
588     * @Param int the number of nested diamond operators
589     * @return the number of token to skip in originalElements list
590     */
591    private int getIndexToNextTokenElement(TokenTextElement elementint nestedDiamondOperator) {
592        int step = 0// number of token to skip
593        Optional<JavaTokennext = element.getToken().getNextToken();
594        if (!next.isPresent()) return step;
595        // because there is a token, first we need to increment the number of token to skip 
596        step++;
597        // manage nested diamond operators by incrementing the level on LT token and decrementing on GT
598        JavaToken token = next.get();
599        Kind kind = Kind.valueOf(token.getKind());
600        if (isDiamondOperator(kind)) {
601            if (kind.GT.equals(kind))
602                nestedDiamondOperator--;
603            else
604                nestedDiamondOperator++;
605        }
606        // manage the fact where the first token is not a diamond operator but a whitespace
607        // and the end of the token sequence to skip
608        // for example in this declaration List <String> a;
609        if (nestedDiamondOperator == 0 && !next.get().getCategory().isWhitespace())
610            return step;
611        // recursively analyze token to skip
612        return step += getIndexToNextTokenElement(new TokenTextElement(token), nestedDiamondOperator);
613    }
614    
615    /*
616     * Returns true if the token is possibly a diamond operator
617     */
618    private boolean isDiamondOperator(Kind kind) {
619        return kind.GT.equals(kind) || kind.LT.equals(kind);
620    }
621
622    private boolean openBraceWasOnSameLine() {
623        int index = originalIndex;
624        while (index >= 0 && !nodeText.getTextElement(index).isNewline()) {
625            if (nodeText.getTextElement(index).isToken(LBRACE)) {
626                return true;
627            }
628            index--;
629        }
630        return false;
631    }
632
633    private boolean wasSpaceBetweenBraces() {
634        return nodeText.getTextElement(originalIndex).isToken(RBRACE)
635                && doWeHaveLeftBraceFollowedBySpace(originalIndex - 1)
636                && (diffIndex < 2 || !diffElements.get(diffIndex - 2).isRemoved());
637    }
638
639    private boolean doWeHaveLeftBraceFollowedBySpace(int index) {
640        index = rewindSpace(index);
641        return nodeText.getElements().get(index).isToken(LBRACE);
642    }
643
644    private int rewindSpace(int index) {
645        if (index <= 0) {
646            return index;
647        }
648        if (nodeText.getElements().get(index).isWhiteSpace()) {
649            return rewindSpace(index - 1);
650        } else {
651            return index;
652        }
653    }
654
655    private boolean nextIsRightBrace(int index) {
656        List<TextElementelements = originalElements.subList(indexoriginalElements.size());
657        for(TextElement element : elements) {
658            if (!element.isSpaceOrTab()) {
659                return element.isToken(RBRACE);
660            }
661        }
662        return false;
663    }
664
665    private void applyAddedDiffElement(Added added) {
666        if (added.isIndent()) {
667            for (int i=0;i<STANDARD_INDENTATION_SIZE;i++){
668                indentation.add(new TokenTextElement(GeneratedJavaParserConstants.SPACE));
669            }
670            addedIndentation = true;
671            diffIndex++;
672            return;
673        }
674        if (added.isUnindent()) {
675            for (int i = 0i<STANDARD_INDENTATION_SIZE && !indentation.isEmpty(); i++){
676                indentation.remove(indentation.size() - 1);
677            }
678            addedIndentation = false;
679            diffIndex++;
680            return;
681        }
682
683        TextElement addedTextElement = added.toTextElement();
684        boolean used = false;
685        boolean isPreviousElementNewline = (originalIndex > 0) && originalElements.get(originalIndex - 1).isNewline();
686        if (isPreviousElementNewline) {
687            List<TextElementelements = processIndentation(indentationoriginalElements.subList(0originalIndex - 1));
688            boolean nextIsRightBrace = nextIsRightBrace(originalIndex);
689            for (TextElement e : elements) {
690                if (!nextIsRightBrace
691                        && e instanceof TokenTextElement
692                        && originalElements.get(originalIndex).isToken(((TokenTextElement)e).getTokenKind())) {
693                    originalIndex++;
694                } else {
695                    nodeText.addElement(originalIndex++, e);
696                }
697            }
698        } else if (isAfterLBrace(nodeTextoriginalIndex) && !isAReplacement(diffIndex)) {
699            if (addedTextElement.isNewline()) {
700                used = true;
701            }
702            nodeText.addElement(originalIndex++, new TokenTextElement(TokenTypes.eolTokenKind()));
703            // This remove the space in "{ }" when adding a new line
704            while (originalIndex >= 2 && originalElements.get(originalIndex - 2).isSpaceOrTab()) {
705                originalElements.remove(originalIndex - 2);
706                originalIndex--;
707            }
708            for (TextElement e : processIndentation(indentationoriginalElements.subList(0originalIndex - 1))) {
709                nodeText.addElement(originalIndex++, e);
710            }
711            // Indentation is painful...
712            // Sometimes we want to force indentation: this is the case when indentation was expected but
713            // was actually not there. For example if we have "{ }" we would expect indentation but it is
714            // not there, so when adding new elements we force it. However if the indentation has been
715            // inserted by us in this transformation we do not want to insert it again
716            if (!addedIndentation) {
717                for (TextElement e : indentationBlock()) {
718                    nodeText.addElement(originalIndex++, e);
719                }
720            }
721        }
722
723        if (!used) {
724            // Handling trailing comments
725            boolean sufficientTokensRemainToSkip = nodeText.numberOfElements() > originalIndex + 2;
726            boolean currentIsAComment = nodeText.getTextElement(originalIndex).isComment();
727            boolean previousIsAComment = originalIndex > 0 && nodeText.getTextElement(originalIndex - 1).isComment();
728            boolean currentIsNewline = nodeText.getTextElement(originalIndex).isNewline();
729
730            if (sufficientTokensRemainToSkip && currentIsAComment) {
731                // Need to get behind the comment:
732                originalIndex += 2// FIXME: Why 2? This comment and the next newline?
733                nodeText.addElement(originalIndexaddedTextElement); // Defer originalIndex increment
734
735                // We want to adjust the indentation while considering the new element that we added
736                originalIndex = adjustIndentation(indentationnodeTextoriginalIndexfalse);
737                originalIndex++; // Now we can increment
738            } else if (currentIsNewline && previousIsAComment) {
739                /*
740                 * Manage the case where we want to add an element, after an expression which is followed by a comment on the same line.
741                 * This is not the same case as the one who handles the trailing comments, because in this case the node text element is a new line (not a comment)
742                 * For example : {@code private String a; // this is a }
743                 */
744                originalIndex++; // Insert after the new line which follows this comment.
745
746                // We want to adjust the indentation while considering the new element that we added
747                originalIndex = adjustIndentation(indentationnodeTextoriginalIndexfalse);
748                nodeText.addElement(originalIndexaddedTextElement); // Defer originalIndex increment
749
750                originalIndex++; // Now we can increment.
751            } else {
752                nodeText.addElement(originalIndexaddedTextElement);
753                originalIndex++;
754            }
755        }
756
757        if (addedTextElement.isNewline()) {
758            boolean followedByUnindent = isFollowedByUnindent(diffElementsdiffIndex);
759            boolean nextIsRightBrace = nextIsRightBrace(originalIndex);
760            boolean nextIsNewLine = nodeText.getTextElement(originalIndex).isNewline();
761            if ((!nextIsNewLine && !nextIsRightBrace) || followedByUnindent) {
762                originalIndex = adjustIndentation(indentationnodeTextoriginalIndexfollowedByUnindent);
763            }
764        }
765
766        diffIndex++;
767    }
768
769    private String tokenDescription(int kind) {
770        return GeneratedJavaParserConstants.tokenImage[kind];
771    }
772
773    private Map<IntegerIntegergetCorrespondanceBetweenNextOrderAndPreviousOrder(CsmMix elementsFromPreviousOrderCsmMix elementsFromNextOrder) {
774        Map<IntegerIntegercorrespondanceBetweenNextOrderAndPreviousOrder = new HashMap<>();
775
776        List<CsmElementnextOrderElements = elementsFromNextOrder.getElements();
777        List<CsmElementpreviousOrderElements = elementsFromPreviousOrder.getElements();
778        WrappingRangeIterator piNext = new WrappingRangeIterator(previousOrderElements.size());
779
780        for (int ni = 0ni < nextOrderElements.size(); ni++) {
781            boolean found = false;
782            CsmElement ne = nextOrderElements.get(ni);
783
784            for (int counter = 0counter < previousOrderElements.size() && !foundcounter++) {
785                Integer pi = piNext.next();
786                CsmElement pe = previousOrderElements.get(pi);
787                if (!correspondanceBetweenNextOrderAndPreviousOrder.values().contains(pi)
788                        && DifferenceElementCalculator.matching(nepe)) {
789                    found = true;
790                    correspondanceBetweenNextOrderAndPreviousOrder.put(nipi);
791                }
792            }
793        }
794
795        return correspondanceBetweenNextOrderAndPreviousOrder;
796    }
797
798    private boolean isFollowedByUnindent(List<DifferenceElementdiffElementsint diffIndex) {
799        return (diffIndex + 1) < diffElements.size()
800                && diffElements.get(diffIndex + 1).isAdded()
801                && diffElements.get(diffIndex + 1).getElement() instanceof CsmUnindent;
802    }
803
804    private List<IntegerfindIndexOfCorrespondingNodeTextElement(List<CsmElementelementsNodeText nodeTextint startIndexNode node) {
805        List<IntegercorrespondingIndices = new ArrayList<>();
806        for (ListIterator<CsmElementcsmElementListIterator = elements.listIterator(); csmElementListIterator.hasNext(); ) {
807
808            int previousCsmElementIndex = csmElementListIterator.previousIndex();
809            CsmElement csmElement = csmElementListIterator.next();
810            int nextCsmElementIndex = csmElementListIterator.nextIndex();
811
812            Map<MatchClassificationIntegerpotentialMatches = new EnumMap<>(MatchClassification.class);
813            for (int i = startIndexi < nodeText.getElements().size(); i++){
814                if (!correspondingIndices.contains(i)) {
815                    TextElement textElement = nodeText.getTextElement(i);
816
817                    boolean isCorresponding = isCorrespondingElement(textElementcsmElementnode);
818
819                    if (isCorresponding) {
820                        boolean hasSamePreviousElement = false;
821                        if (i > 0 && previousCsmElementIndex > -1) {
822                            TextElement previousTextElement = nodeText.getTextElement(i - 1);
823
824                            hasSamePreviousElement = isCorrespondingElement(previousTextElementelements.get(previousCsmElementIndex), node);
825                        }
826
827                        boolean hasSameNextElement = false;
828                        if (i < nodeText.getElements().size() - 1 && nextCsmElementIndex < elements.size()) {
829                            TextElement nextTextElement = nodeText.getTextElement(i + 1);
830
831                            hasSameNextElement = isCorrespondingElement(nextTextElementelements.get(nextCsmElementIndex), node);
832                        }
833
834                        if (hasSamePreviousElement && hasSameNextElement) {
835                            potentialMatches.putIfAbsent(MatchClassification.ALLi);
836                        } else if (hasSamePreviousElement) {
837                            potentialMatches.putIfAbsent(MatchClassification.PREVIOUS_AND_SAMEi);
838                        } else if (hasSameNextElement) {
839                            potentialMatches.putIfAbsent(MatchClassification.NEXT_AND_SAMEi);
840                        } else {
841                            potentialMatches.putIfAbsent(MatchClassification.SAME_ONLYi);
842                        }
843                    } else if (isAlmostCorrespondingElement(textElementcsmElementnode)) {
844                        potentialMatches.putIfAbsent(MatchClassification.ALMOSTi);
845                    }
846                }
847            }
848
849            // Prioritize the matches from best to worst
850            Optional<MatchClassificationbestMatchKey = potentialMatches.keySet().stream()
851                    .min(Comparator.comparing(MatchClassification::getPriority));
852
853            if (bestMatchKey.isPresent()) {
854                correspondingIndices.add(potentialMatches.get(bestMatchKey.get()));
855            } else {
856                correspondingIndices.add(-1);
857            }
858        }
859
860        return correspondingIndices;
861    }
862
863    private enum MatchClassification {
864        ALL(1), PREVIOUS_AND_SAME(2), NEXT_AND_SAME(3), SAME_ONLY(4), ALMOST(5);
865
866        private final int priority;
867
868        MatchClassification(int priority) {
869            this.priority = priority;
870        }
871
872        int getPriority() {
873            return priority;
874        }
875    }
876
877    private boolean isCorrespondingElement(TextElement textElementCsmElement csmElementNode node) {
878        if (csmElement instanceof CsmToken) {
879            CsmToken csmToken = (CsmToken)csmElement;
880            if (textElement instanceof TokenTextElement) {
881                TokenTextElement tokenTextElement = (TokenTextElement)textElement;
882                return tokenTextElement.getTokenKind() == csmToken.getTokenType() && tokenTextElement.getText().equals(csmToken.getContent(node));
883            }
884        } else if (csmElement instanceof CsmChild) {
885            CsmChild csmChild = (CsmChild)csmElement;
886            if (textElement instanceof ChildTextElement) {
887                ChildTextElement childTextElement = (ChildTextElement)textElement;
888                return childTextElement.getChild() == csmChild.getChild();
889            }
890        } else {
891            throw new UnsupportedOperationException();
892        }
893
894        return false;
895    }
896
897    private boolean isAlmostCorrespondingElement(TextElement textElementCsmElement csmElementNode node) {
898        if (isCorrespondingElement(textElementcsmElementnode)) {
899            return false;
900        }
901        return textElement.isWhiteSpace() && csmElement instanceof CsmToken && ((CsmToken)csmElement).isWhiteSpace();
902    }
903
904    private int adjustIndentation(List<TokenTextElementindentationNodeText nodeTextint nodeTextIndexboolean followedByUnindent) {
905        List<TextElementindentationAdj = processIndentation(indentationnodeText.getElements().subList(0nodeTextIndex - 1));
906        if (nodeTextIndex < nodeText.getElements().size() && nodeText.getElements().get(nodeTextIndex).isToken(RBRACE)) {
907            indentationAdj = indentationAdj.subList(0indentationAdj.size() - Math.min(STANDARD_INDENTATION_SIZEindentationAdj.size()));
908        } else if (followedByUnindent) {
909            indentationAdj = indentationAdj.subList(0Math.max(0indentationAdj.size() - STANDARD_INDENTATION_SIZE));
910        }
911        for (TextElement e : indentationAdj) {
912            if ((nodeTextIndexnodeText.getElements().size()) && nodeText.getElements().get(nodeTextIndex).isSpaceOrTab()) {
913                nodeTextIndex++;
914            } else {
915                nodeText.getElements().add(nodeTextIndex++, e);
916            }
917        }
918        if (nodeTextIndex < 0) {
919            throw new IllegalStateException();
920        }
921        return nodeTextIndex;
922    }
923
924    private boolean isAReplacement(int diffIndex) {
925        return (diffIndex > 0) && diffElements.get(diffIndex) instanceof Added && diffElements.get(diffIndex - 1) instanceof Removed;
926    }
927
928    private boolean isReplaced(int diffIndex) {
929        return (diffIndex < diffElements.size() - 1) && diffElements.get(diffIndex + 1) instanceof Added && diffElements.get(diffIndex) instanceof Removed;
930    }
931
932    @Override
933    public String toString() {
934        return "Difference{" + diffElements + '}';
935    }
936}
937
MembersX
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:nextCsmElementIndex
Difference:getIndexToNextTokenElement
Difference:extractReshuffledDiffElements:Block:Block:Block:diffElIterator
Difference:extractReshuffledDiffElements:Block:Block:Block:correspondanceBetweenNextOrderAndPreviousOrder
Difference:applyLeftOverOriginalElements
Difference:extractReshuffledDiffElements:Block:Block:Block:reshuffled
Difference:adjustIndentation
Difference:apply:Block:Block:isLeftOverDiffElement
Difference:applyLeftOverOriginalElements:Block:isLeftOverElement
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:Block:Block:Block:hasSameNextElement
Difference:adjustIndentation:Block:indentationAdj
Difference:getIndexToNextTokenElement:Block:token
Difference:extractReshuffledDiffElements:Block:Block:Block:nodeTextIndexOfPreviousElements
Difference:applyAddedDiffElement:Block:used
Difference:findIndexOfCorrespondingNodeTextElement
Difference:originalElements
Difference:rewindSpace
Difference:nextIsRightBrace:Block:elements
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:csmElement
Difference:apply
Difference:considerEnforcingIndentation:Block:res
Difference:isFollowedByUnindent
Difference:extractReshuffledDiffElements:Block:Block:Block:Block:Block:Block:originalCSMElement
Difference:applyLeftOverDiffElements
Difference:applyLeftOverDiffElements:Block:Block:Block:addedElement
Difference:groupConsecutiveRemovedElements
Difference:MatchClassification:ALMOST
Difference:diffIndex
Difference:isNodeWithTypeArguments:Block:child
Difference:isCorrespondingElement:Block:Block:Block:tokenTextElement
Difference:extractReshuffledDiffElements:Block:Block:diffElement
Difference:isCorrespondingElement:Block:Block:csmChild
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:Block:Block:Block:hasSamePreviousElement
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:piNext
Difference:applyAddedDiffElement
Difference:extractReshuffledDiffElements:Block:Block:Block:elementsToAddBeforeGivenOriginalCSMElement
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:Block:ne
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:correspondanceBetweenNextOrderAndPreviousOrder
Difference:findIndexOfCorrespondingNodeTextElement:Block:correspondingIndices
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:Block:Block:textElement
Difference:considerEnforcingIndentation:Block:hasOnlyWsBefore
Difference:extractReshuffledDiffElements:Block:Block:Block:lastNodeTextIndex
Difference:groupConsecutiveRemovedElements:Block:removedElementsMap
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:bestMatchKey
Difference:applyKeptDiffElement
Difference:applyLeftOverDiffElements:Block:isLeftOverElement
Difference:getIndexToNextTokenElement:Block:next
Difference:toString
Difference:Difference
Difference:indentation
Difference:MatchClassification:priority
Difference:extractReshuffledDiffElements:Block:Block:Block:Block:Block:Block:toBeKept
Difference:apply:Block:Block:Block:diffElement
Difference:combineRemovedElementsToRemovedGroups
Difference:MatchClassification:ALL
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:Block:Block:isCorresponding
Difference:openBraceWasOnSameLine
Difference:applyKeptDiffElement:Block:Block:Block:step
Difference:cleanTheLineOfLeftOverSpace:Block:Block:lastElementIndex
Difference:applyRemovedDiffElement
Difference:isAlmostCorrespondingElement
Difference:apply:Block:Block:Block:Block:Block:removed
Difference:groupConsecutiveRemovedElements:Block:firstElement
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:Block:Block:Block:Block:previousTextElement
Difference:groupConsecutiveRemovedElements:Block:Block:diffElement
Difference:isDiamondOperator
Difference:considerEnforcingIndentation
Difference:applyRemovedDiffElement:Block:Block:Block:comment
Difference:diffElements
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:Block:Block:pe
Difference:isAReplacement
Difference:applyLeftOverDiffElements:Block:Block:Block:kept
Difference:getIndexToNextTokenElement:Block:step
Difference:applyAddedDiffElement:Block:Block:sufficientTokensRemainToSkip
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:Block:Block:pi
Difference:indentationBlock:Block:res
Difference:extractReshuffledDiffElements:Block:Block:Block:Block:Block:originalCsmIndex
Difference:isCorrespondingElement:Block:Block:Block:childTextElement
Difference:MatchClassification:SAME_ONLY
Difference:processIndentation:Block:afterNl
Difference:applyKeptDiffElement:Block:Block:originalTextToken
Difference:processIndentation
Difference:applyLeftOverOriginalElements:Block:Block:originalElement
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:potentialMatches
Difference:extractReshuffledDiffElements:Block:Block:Block:Block:value
Difference:applyAddedDiffElement:Block:Block:previousIsAComment
Difference:openBraceWasOnSameLine:Block:index
Difference:applyAddedDiffElement:Block:Block:nextIsRightBrace
Difference:MatchClassification:NEXT_AND_SAME
Difference:apply:Block:Block:isLeftOverOriginalElement
Difference:apply:Block:Block:Block:Block:originalElementIsChild
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:previousOrderElements
Difference:extractReshuffledDiffElements:Block:Block:Block:elementsFromPreviousOrder
Difference:cleanTheLineOfLeftOverSpace
Difference:applyAddedDiffElement:Block:Block:nextIsNewLine
Difference:getIndexToNextTokenElement:Block:kind
Difference:tokenDescription
Difference:isCorrespondingElement
Difference:combineRemovedElementsToRemovedGroups:Block:map
Difference:extractReshuffledDiffElements:Block:Block:Block:elementsToBeAddedAtTheEnd
Difference:applyRemovedDiffElement:Block:Block:originalElementChild
Difference:cleanTheLineOfLeftOverSpace:Block:Block:indentation
Difference:isNodeWithTypeArguments:Block:typeArgs
Difference:applyAddedDiffElement:Block:Block:currentIsNewline
Difference:MatchClassification:PREVIOUS_AND_SAME
Difference:STANDARD_INDENTATION_SIZE
Difference:nodeText
Difference:applyAddedDiffElement:Block:Block:currentIsAComment
Difference:isReplaced
Difference:extractReshuffledDiffElements:Block:Block:Block:nodeTextIndexToPreviousCSMIndex
Difference:combineRemovedElementsToRemovedGroups:Block:removedElementsMap
Difference:applyAddedDiffElement:Block:addedTextElement
Difference:apply:Block:Block:Block:Block:originalElementIsToken
Difference:node
Difference:applyAddedDiffElement:Block:isPreviousElementNewline
Difference:nextIsRightBrace
Difference:apply:Block:Block:Block:Block:originalElement
Difference:originalIndex
Difference:apply:Block:removedGroups
Difference:doWeHaveLeftBraceFollowedBySpace
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:Block:found
Difference:isNodeWithTypeArguments:Block:csmElem
Difference:combineRemovedElementsToRemovedGroups:Block:removedGroups
Difference:extractReshuffledDiffElements
Difference:extractReshuffledDiffElements:Block:Block:Block:elementsFromNextOrder
Difference:extractReshuffledDiffElements:Block:Block:Block:nextOrderElements
Difference:indentationBlock
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:previousCsmElementIndex
Difference:processIndentation:Block:res
Difference:extractReshuffledDiffElements:Block:Block:Block:Block:Block:Block:indexOfOriginalCSMElement
Difference:applyAddedDiffElement:Block:Block:followedByUnindent
Difference:MatchClassification:MatchClassification
Difference:MatchClassification:getPriority
Difference:wasSpaceBetweenBraces
Difference:isCorrespondingElement:Block:Block:csmToken
Difference:addedIndentation
Difference:applyAddedDiffElement:Block:Block:elements
Difference:findIndexOfCorrespondingNodeTextElement:Block:Block:Block:Block:Block:Block:nextTextElement
Difference:isAfterLBrace
Difference:getCorrespondanceBetweenNextOrderAndPreviousOrder:Block:nextOrderElements
Difference:isNodeWithTypeArguments
Difference:applyLeftOverDiffElements:Block:Block:diffElement
Members
X