blob: edbce4c0e262f8da8be5cefa9cdc76cc353dab77 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.source.save;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import static com.sun.source.doctree.DocTree.Kind.RETURN;
import com.sun.source.tree.*;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.DocSourcePositions;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import org.netbeans.api.java.source.support.ErrorAwareTreeScanner;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.*;
import static com.sun.tools.javac.code.Flags.*;
import com.sun.tools.javac.tree.DCTree;
import com.sun.tools.javac.tree.DCTree.DCAttribute;
import com.sun.tools.javac.tree.DCTree.DCAuthor;
import com.sun.tools.javac.tree.DCTree.DCComment;
import com.sun.tools.javac.tree.DCTree.DCDeprecated;
import com.sun.tools.javac.tree.DCTree.DCDocComment;
import com.sun.tools.javac.tree.DCTree.DCDocRoot;
import com.sun.tools.javac.tree.DCTree.DCEndElement;
import com.sun.tools.javac.tree.DCTree.DCEntity;
import com.sun.tools.javac.tree.DCTree.DCErroneous;
import com.sun.tools.javac.tree.DCTree.DCIdentifier;
import com.sun.tools.javac.tree.DCTree.DCInheritDoc;
import com.sun.tools.javac.tree.DCTree.DCLink;
import com.sun.tools.javac.tree.DCTree.DCLiteral;
import com.sun.tools.javac.tree.DCTree.DCParam;
import com.sun.tools.javac.tree.DCTree.DCReference;
import com.sun.tools.javac.tree.DCTree.DCReturn;
import com.sun.tools.javac.tree.DCTree.DCSee;
import com.sun.tools.javac.tree.DCTree.DCSerial;
import com.sun.tools.javac.tree.DCTree.DCSerialData;
import com.sun.tools.javac.tree.DCTree.DCSerialField;
import com.sun.tools.javac.tree.DCTree.DCSince;
import com.sun.tools.javac.tree.DCTree.DCStartElement;
import com.sun.tools.javac.tree.DCTree.DCText;
import com.sun.tools.javac.tree.DCTree.DCThrows;
import com.sun.tools.javac.tree.DCTree.DCUnknownBlockTag;
import com.sun.tools.javac.tree.DCTree.DCUnknownInlineTag;
import com.sun.tools.javac.tree.DCTree.DCValue;
import com.sun.tools.javac.tree.DCTree.DCVersion;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssert;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCAssignOp;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCContinue;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
import com.sun.tools.javac.tree.JCTree.JCErroneous;
import com.sun.tools.javac.tree.JCTree.JCExports;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCForLoop;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
import com.sun.tools.javac.tree.JCTree.JCLambda;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMemberReference;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
import com.sun.tools.javac.tree.JCTree.JCNewArray;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCOpens;
import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
import com.sun.tools.javac.tree.JCTree.JCParens;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCProvides;
import com.sun.tools.javac.tree.JCTree.JCRequires;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSwitch;
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCTypeUnion;
import com.sun.tools.javac.tree.JCTree.JCUnary;
import com.sun.tools.javac.tree.JCTree.JCUses;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.tree.JCTree.Tag;
import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
import com.sun.tools.javac.tree.Pretty;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import com.sun.tools.javac.util.Position;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
import static java.util.logging.Level.*;
import java.util.logging.Logger;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.editor.document.AtomicLockDocument;
import org.netbeans.api.editor.document.LineDocumentUtils;
import org.netbeans.api.java.lexer.JavaTokenId;
import org.netbeans.api.java.source.CodeStyle;
import org.netbeans.api.java.source.Comment;
import org.netbeans.api.java.source.Comment.Style;
import org.netbeans.api.java.source.TreeUtilities;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.api.lexer.Language;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.editor.indent.api.Indent;
import org.netbeans.modules.java.source.builder.CommentHandlerService;
import org.netbeans.modules.java.source.builder.CommentSetImpl;
import org.netbeans.modules.java.source.pretty.VeryPretty;
import org.netbeans.modules.java.source.query.CommentHandler;
import org.netbeans.modules.java.source.query.CommentSet;
import org.netbeans.modules.java.source.save.ListMatcher.Operation;
import org.netbeans.modules.java.source.save.ListMatcher.ResultItem;
import org.netbeans.modules.java.source.save.ListMatcher.Separator;
import static org.netbeans.modules.java.source.save.PositionEstimator.*;
import org.netbeans.modules.java.source.save.PositionEstimator.Direction;
import org.netbeans.modules.java.source.transform.FieldGroupTree;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.NbCollections;
import javax.lang.model.type.TypeKind;
import org.netbeans.modules.java.source.TreeShims;
public class CasualDiff {
public static boolean OLD_TREES_VERBATIM = Boolean.parseBoolean(System.getProperty(WorkingCopy.class.getName() + ".keep-old-trees", "true"));
protected final Collection<Diff> diffs;
protected CommentHandler comments;
protected JCCompilationUnit oldTopLevel;
protected TreeUtilities treeUtilities;
protected final DiffContext diffContext;
private TokenSequence<JavaTokenId> tokenSequence;
private String origText;
private VeryPretty printer;
private final Context context;
private final Names names;
private static final Logger LOG = Logger.getLogger(CasualDiff.class.getName());
public static final int GENERATED_MEMBER = 1<<24;
private Map<Integer, String> diffInfo = new HashMap<>();
private final Map<Tree, ?> tree2Tag;
private final Map<Object, int[]> tag2Span;
private final Set<Tree> oldTrees;
private final Map<Tree, DocCommentTree> tree2Doc;
// used for diffing var def, when parameter is printed, annotation of
// such variable should not provide new line at the end.
private boolean parameterPrint = false;
private boolean enumConstantPrint = false;
/**
* Aliases places in the new text with block sequence boundaries in the old text. The diff can then get information
* on how guarded blocks (or other divisor of the source) is mapped into the changed text and not generate diffs across
* such a boundary.
*/
private Map<Integer, Integer> blockSequenceMap = new LinkedHashMap<>();
private Iterator<Integer> boundaries;
private int nextBlockBoundary = -1;
protected CasualDiff(Context context, DiffContext diffContext, TreeUtilities treeUtilities, Map<Tree, ?> tree2Tag, Map<Tree, DocCommentTree> tree2Doc, Map<?, int[]> tag2Span, Set<Tree> oldTrees) {
diffs = new LinkedHashSet<Diff>();
comments = CommentHandlerService.instance(context);
this.treeUtilities = treeUtilities;
this.diffContext = diffContext;
this.tokenSequence = diffContext.tokenSequence;
this.origText = diffContext.origText;
this.context = context;
this.names = Names.instance(context);
this.tree2Tag = tree2Tag;
this.tree2Doc = tree2Doc;
this.tag2Span = (Map<Object, int[]>) tag2Span;//XXX
printer = new VeryPretty(diffContext, diffContext.style, tree2Tag, tree2Doc, tag2Span, origText);
printer.oldTrees = oldTrees;
this.oldTrees = oldTrees;
}
public static Collection<Diff> diff(Context context,
DiffContext diffContext,
TreeUtilities treeUtilities,
TreePath oldTreePath,
JCTree newTree,
Map<Integer, String> userInfo,
Map<Tree, ?> tree2Tag,
Map<Tree, DocCommentTree> tree2Doc,
Map<?, int[]> tag2Span,
Set<Tree> oldTrees)
{
final CasualDiff td = new CasualDiff(context, diffContext, treeUtilities, tree2Tag, tree2Doc, tag2Span, oldTrees);
JCTree oldTree = (JCTree) oldTreePath.getLeaf();
td.oldTopLevel = (JCCompilationUnit) (oldTree.getKind() == Kind.COMPILATION_UNIT ? oldTree : diffContext.origUnit);
for (Tree t : oldTreePath) {
if (t == oldTree) continue;
List<? extends Tree> embeddedElements;
if (TreeUtilities.CLASS_TREE_KINDS.contains(t.getKind())) {
embeddedElements = ((ClassTree) t).getMembers();
} else if (t.getKind() == Kind.BLOCK) {
embeddedElements = ((BlockTree) t).getStatements();
} else {
continue;
}
embeddedElements = td.filterHidden(NbCollections.checkedListByCopy(embeddedElements, JCTree.class, false));
if (embeddedElements.isEmpty()) {
int indent = getOldIndent(diffContext, t);
if (indent < 0) {
td.printer.indent();
} else {
td.printer.setIndent(indent);
td.printer.indent();
break;
}
} else {
int indent = getOldIndent(diffContext, embeddedElements.get(0));
if (indent < 0) {
td.printer.indent();
} else {
td.printer.setIndent(indent);
break;
}
}
}
if (org.netbeans.api.java.source.TreeUtilities.CLASS_TREE_KINDS.contains(oldTree.getKind()) && oldTreePath.getParentPath().getLeaf().getKind() == Kind.NEW_CLASS) {
td.anonClass = true;
}
int[] bounds = td.getCommentCorrectedBounds(oldTree);
boolean isCUT = oldTree.getKind() == Kind.COMPILATION_UNIT;
int start = isCUT ? 0 : bounds[0];
String origText = td.origText;
int end = isCUT ? origText.length() : bounds[1];
//#177660: LineMap is probably not updated correctly for partial reparse, workaround:
int lineStart = start;
while (lineStart > 0 && origText.charAt(lineStart - 1) != '\n') {
lineStart--;
}
//was:
// int ln = td.oldTopLevel.lineMap.getLineNumber(start);
// int lineStart = td.oldTopLevel.lineMap.getStartPosition(ln);
td.printer.setInitialOffset(lineStart);
Tree current = oldTree;
for (Tree t : oldTreePath) {
if (t.getKind() == Kind.METHOD) {
MethodTree mt = (MethodTree) t;
for (Tree p : mt.getParameters()) {
if (p == current) {
td.parameterPrint = true;
}
}
break;
} else if (t.getKind() == Kind.LAMBDA_EXPRESSION) {
// special hack for lambda expressions: annotations should appear inline, rather than create newlines
for (Tree p : ((LambdaExpressionTree)t).getParameters()) {
if (p == current) {
td.parameterPrint = true;
}
}
break;
} else if (t.getKind() == Kind.VARIABLE) {
JCVariableDecl vt = (JCVariableDecl) t;
if ((vt.mods.flags & ENUM) != 0 && vt.init == current)
td.enumConstantPrint = true;
}
current = t;
}
if (oldTree.getKind() == Kind.MODIFIERS || oldTree.getKind() == Kind.METHOD || (!td.parameterPrint && oldTree.getKind() == Kind.VARIABLE)) {
td.tokenSequence.move(start);
if (td.tokenSequence.movePrevious() && td.tokenSequence.token().id() == JavaTokenId.WHITESPACE) {
String text = td.tokenSequence.token().text().toString();
int index = text.lastIndexOf('\n');
start = td.tokenSequence.offset();
if (index > -1) {
start += index + 1;
}
}
}
td.copyTo(lineStart, start, td.printer);
td.diffTree(oldTreePath, newTree, new int[] {start, bounds[1]});
String resultSrc = td.printer.toString().substring(start - lineStart);
if (!td.printer.reindentRegions.isEmpty()) {
// must add region boundaries to tag2span, since the text may be reformatted.
List<SectKey> keys = new ArrayList<>(td.blockSequenceMap.size());
for (Map.Entry<Integer, Integer> e : td.blockSequenceMap.entrySet()) {
int x = e.getValue();
SectKey k = new SectKey(x);
keys.add(k);
td.tag2Span.put(k, new int[] {x, x});
}
try {
String toParse = origText.substring(0, start) + resultSrc + origText.substring(end);
final Document doc = LineDocumentUtils.createDocument("text/x-java");
doc.insertString(0, toParse, null);
doc.putProperty(Language.class, JavaTokenId.language());
doc.putProperty(Document.StreamDescriptionProperty, diffContext.file);
javax.swing.text.Position startPos = doc.createPosition(start);
javax.swing.text.Position endPos = doc.createPosition(start + resultSrc.length());
Map<Object, javax.swing.text.Position[]> spans = new IdentityHashMap<>(td.tag2Span.size());
for (Entry<Object, int[]> e : td.tag2Span.entrySet()) {
spans.put(e.getKey(), new javax.swing.text.Position[] {
doc.createPosition(e.getValue()[0]),
doc.createPosition(e.getValue()[1])
});
}
final Indent i = Indent.get(doc);
i.lock();
AtomicLockDocument adoc = LineDocumentUtils.asRequired(doc, AtomicLockDocument.class);
try {
Runnable r = new Runnable() {
@Override public void run() {
try {
javax.swing.text.Position[] regions = new javax.swing.text.Position[td.printer.reindentRegions.size() * 2];
int idx = 0;
for (int[] region : td.printer.reindentRegions) {
regions[idx] = doc.createPosition(region[0]);
regions[idx + 1] = doc.createPosition(region[1]);
idx += 2;
}
for (int j = 0; j < regions.length; j += 2) {
i.reindent(regions[j].getOffset(), regions[j + 1].getOffset());
}
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
};
adoc.runAtomic(r);
} finally {
i.unlock();
}
resultSrc = doc.getText(startPos.getOffset(), endPos.getOffset() - startPos.getOffset());
for (Entry<Object, javax.swing.text.Position[]> e : spans.entrySet()) {
int[] span = td.tag2Span.get(e.getKey());
span[0] = e.getValue()[0].getOffset();
span[1] = e.getValue()[1].getOffset();
}
// fixup the block sequence map after formatting, expecting that the map was not changed during formatting
Iterator<SectKey> it = keys.iterator();
for (Map.Entry<Integer, Integer> e : td.blockSequenceMap.entrySet()) {
SectKey k = it.next();
int[] span = td.tag2Span.get(k);
e.setValue(span[0]);
}
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
String originalText = isCUT ? origText : origText.substring(start, end);
userInfo.putAll(td.diffInfo);
return td.checkDiffs(DiffUtilities.diff(originalText, resultSrc, start,
td.readSections(originalText.length(), resultSrc.length(), lineStart, start), lineStart));
}
private static class SectKey {
private int off;
SectKey(int off) { this.off = off; }
}
/**
* Reads the section map. While the printer produced the text matching the region
* starting at 'start', the diff will only cover text starting and textStart, which may
* be in the middle of the line. the blockSequenceMap was filled by the printer,
* so offsets may need to be moved backwards.
*
* @param l1 length of the original text
* @param l2 length of the new text
* @param printerStart printer start
* @param diffStart
* @return
*/
private int[] readSections(int l1, int l2, int printerStart, int diffStart) {
Map<Integer, Integer> seqMap = blockSequenceMap;
if (seqMap.isEmpty()) {
// must offset the lengths, they come from the origtext/resultsrc, which may be already
// only substrings of the printed area.
int delta = diffStart - printerStart;
return new int[] { l1 + delta, l2 + delta };
}
int[] res = new int[seqMap.size() * 2];
int p = 0;
for (Map.Entry<Integer, Integer> en : seqMap.entrySet()) {
int point = en.getKey();
if (point < printerStart || point > diffStart + l2) {
continue;
}
res[p++] = en.getKey();
res[p++] = en.getValue();
}
return res;
}
private List<Diff> checkDiffs(List<Diff> theDiffs) {
if (theDiffs != null) {
for (Diff d : theDiffs) {
if (diffContext.positionConverter.getOriginalPosition(d.getPos()) > diffContext.textLength ||
diffContext.positionConverter.getOriginalPosition(d.getEnd()) > diffContext.textLength) {
LOG.warning("Invalid diff: " + d);
}
}
}
return theDiffs;
}
public static Collection<Diff> diff(Context context,
DiffContext diffContext,
TreeUtilities treeUtilities,
List<? extends ImportTree> original,
List<? extends ImportTree> nue,
Map<Integer, String> userInfo,
Map<Tree, ?> tree2Tag,
Map<Tree, DocCommentTree> tree2Doc,
Map<?, int[]> tag2Span,
Set<Tree> oldTrees)
{
CasualDiff td = new CasualDiff(context, diffContext, treeUtilities, tree2Tag, tree2Doc, tag2Span, oldTrees);
td.oldTopLevel = diffContext.origUnit;
// TODO: the package name actually ends at the end of the name, so the semicolon could be treated as part
// of the diffed list
int start = td.oldTopLevel.getPackage() != null ? td.endPos(td.oldTopLevel.getPackage()) : 0;
//XXX: no-javac-patch:
td.tokenSequence.move(start);
while (td.tokenSequence.movePrevious()) {
if (td.isNoop(td.tokenSequence.token().id())) {
start = td.tokenSequence.offset();
}
}
//XXX: no-javac-patch end
List<JCImport> originalJC = new LinkedList<>();
List<JCImport> nueJC = new LinkedList<>();
for (ImportTree i : original) {
originalJC.add((JCImport) i);
}
for (ImportTree i : nue) {
nueJC.add((JCImport) i);
}
PositionEstimator est = EstimatorFactory.imports(originalJC, nueJC, td.diffContext);
int end = td.diffList(originalJC, nueJC, start, est, Measure.DEFAULT, td.printer);
String resultSrc = td.printer.toString();
if (start > end) {
// diagnostic for the purpose of #200152
LOG.log(INFO, "Illegal values: start = " + start + "; end = " + end + "." +
"Please attach your messages.log to issue #200152 (https://netbeans.org/bugzilla/show_bug.cgi?id=200152)");
LOG.log(INFO, "-----\n" + td.diffContext.origText + "-----\n");
LOG.log(INFO, "Orig imports: " + original);
LOG.log(INFO, "New imports: " + nue);
}
String originalText = td.diffContext.origText.substring(start, end);
userInfo.putAll(td.diffInfo);
return td.checkDiffs(DiffUtilities.diff(originalText, resultSrc, start));
}
public int endPos(JCTree t) {
// compensate for FieldGroupTree, see issue #237574
if (t instanceof FieldGroupTree) {
FieldGroupTree fgt = (FieldGroupTree)t;
VariableTree vt = fgt.getVariables().get(fgt.getVariables().size() - 1);
return TreeInfo.getEndPos((JCTree)vt, oldTopLevel.endPositions);
}
int endPos = TreeInfo.getEndPos(t, oldTopLevel.endPositions);
if (endPos == Position.NOPOS) {
if (t instanceof JCAssign) {
// [NETBEANS-4299], might be a synthetic annotation attribute, try rhs
endPos = TreeInfo.getEndPos(((JCAssign)t).rhs, oldTopLevel.endPositions);
} else {
endPos = getOldPos(t);
}
}
return endPos;
}
private int endPos(com.sun.tools.javac.util.List<? extends JCTree> trees) {
int result = -1;
if (trees.nonEmpty()) {
result = endPos(trees.head);
for (com.sun.tools.javac.util.List <? extends JCTree> l = trees.tail; l.nonEmpty(); l = l.tail) {
result = endPos(l.head);
}
}
return result;
}
private int endPos(List<? extends JCTree> trees) {
if (trees.isEmpty())
return -1;
return endPos(trees.get(trees.size()-1));
}
private int checkLocalPointer(JCTree oldT, JCTree newT, int localPointer) {
// diagnostics for defect #226498: log the tres iff the localPointer is bad
if (localPointer < 0 || localPointer > origText.length()) {
LOG.warning("Invalid localPointer (" + localPointer + "), see defect #226498 and report the log to the issue");
LOG.warning("OldT:" + oldT);
LOG.warning("NewT:" + newT);
LOG.warning("CodeStyle: " + printCodeStyle(diffContext.style));
LOG.warning("origText(" + origText.length() + "): " + origText);
StringWriter sw = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(sw));
LOG.warning("Stacktrace: " + sw.toString());
}
return localPointer;
}
protected void diffTopLevel(JCCompilationUnit oldT, JCCompilationUnit newT, int[] elementBounds) {
oldTopLevel = oldT;
int localPointer = diffPackage(oldT.getPackage(), newT.getPackage(), elementBounds[0]);
PositionEstimator est = EstimatorFactory.imports(oldT.getImports(), newT.getImports(), diffContext);
localPointer = diffList(oldT.getImports(), newT.getImports(), localPointer, est, Measure.DEFAULT, printer);
est = EstimatorFactory.toplevel(oldT.getTypeDecls(), newT.getTypeDecls(), diffContext);
localPointer = diffList(oldT.getTypeDecls(), newT.getTypeDecls(), localPointer, est, Measure.REAL_MEMBER, printer);
checkLocalPointer(oldT, newT, localPointer);
printer.print(origText.substring(localPointer));
}
private static int getOldIndent(DiffContext diffContext, Tree t) {
int offset = (int) diffContext.trees.getSourcePositions().getStartPosition(diffContext.origUnit, t);
if (offset < 0) return -1;
while (offset > 0 && diffContext.origText.charAt(offset - 1) != '\n')
offset--;
int indent = 0;
while (offset < diffContext.origText.length()) {
char c = diffContext.origText.charAt(offset++);
if (c == '\t') {
indent += diffContext.style.getTabSize();
} else if (c == '\n' || !Character.isWhitespace(c)) {
break;
} else {
indent++;
}
}
return indent;
}
private boolean needStar(int localPointer) {
if (localPointer <= 0) {
return false;
}
while (localPointer > 0) {
char c = diffContext.origText.charAt(--localPointer);
if (c == '\n') {
return false;
} else if(!Character.isWhitespace(c)) {
if(localPointer > 3 && diffContext.origText.substring(localPointer-2, localPointer+1).equals("/**")) {
return true;
} else {
return false;
}
}
}
return false;
}
private int adjustToPreviousNewLine(int oldPos, int localPointer) {
int offset = oldPos;
if (offset < 0) {
return localPointer;
}
while (offset > localPointer) {
char c = diffContext.origText.charAt(--offset);
if (c == '\n') {
break;
} else if(c != '*' && !Character.isWhitespace(c)) {
return oldPos;
}
}
return offset;
}
private static enum ChangeKind {
INSERT,
DELETE,
MODIFY,
NOCHANGE;
}
private ChangeKind getChangeKind(Tree oldT, Tree newT) {
if (oldT == newT) {
return ChangeKind.NOCHANGE;
}
if (oldT != null && newT != null) {
return ChangeKind.MODIFY;
}
if (oldT != null) {
return ChangeKind.DELETE;
} else {
return ChangeKind.INSERT;
}
}
protected int diffModuleDef(JCModuleDecl oldT, JCModuleDecl newT, int[] bounds) {
int localPointer = bounds[0];
int[] qualBounds = getBounds(oldT.qualId);
if (oldT.getModuleType() == newT.getModuleType()) {
copyTo(localPointer, qualBounds[0]);
} else {
if (oldT.getModuleType() == ModuleTree.ModuleKind.OPEN) {
//removing "open":
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.OPEN);
copyTo(localPointer, tokenSequence.offset());
} else {
copyTo(localPointer, qualBounds[0]);
printer.print("open ");
}
}
localPointer = diffTree(oldT.qualId, newT.qualId, qualBounds);
int insertHint = oldT.directives.isEmpty() ? endPos(oldT) - 1 : oldT.directives.get(0).getStartPosition() - 1;
tokenSequence.move(insertHint);
tokenSequence.moveNext();
insertHint = moveBackToToken(tokenSequence, insertHint, JavaTokenId.LBRACE) + 1;
int old = printer.indent();
PositionEstimator est = EstimatorFactory.members(oldT.directives, newT.directives, diffContext);
localPointer = copyUpTo(localPointer, insertHint);
// diff inner comments
insertHint = diffInnerComments(oldT, newT, insertHint);
localPointer = diffList(oldT.directives, newT.directives, insertHint, est, Measure.REAL_MEMBER, printer);
printer.undent(old);
if (localPointer != -1 && localPointer < origText.length()) {
if (origText.charAt(localPointer) == '}') {
// another stupid hack
printer.toLeftMargin();
}
copyTo(localPointer, bounds[1]);
}
return bounds[1];
}
protected int diffRequires(JCRequires oldT, JCRequires newT, int[] bounds) {
int localPointer = bounds[0];
// module name
int[] nameBounds = getBounds(oldT.moduleName);
if (oldT.isStaticPhase || oldT.isTransitive) {
JavaTokenId id = moveFwdToOneOfTokens(tokenSequence, localPointer, EnumSet.of(JavaTokenId.STATIC, JavaTokenId.TRANSITIVE));
switch (id) {
case STATIC:
if (!newT.isStaticPhase) {
//removing "static":
copyTo(localPointer, tokenSequence.offset());
tokenSequence.moveNext();
localPointer = tokenSequence.offset() + tokenSequence.token().length();
}
if (oldT.isTransitive == newT.isTransitive) {
copyTo(localPointer, nameBounds[0]);
} else {
if (oldT.isTransitive) {
//removing "transitive":
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.TRANSITIVE);
copyTo(localPointer, tokenSequence.offset());
} else {
copyTo(localPointer, nameBounds[0]);
printer.print("transitive "); //NOI18N
}
}
break;
case TRANSITIVE:
if (!newT.isTransitive) {
//removing "transitive":
copyTo(localPointer, tokenSequence.offset());
tokenSequence.moveNext();
localPointer = tokenSequence.offset() + tokenSequence.token().length();
}
if (oldT.isStaticPhase == newT.isStaticPhase) {
copyTo(localPointer, nameBounds[0]);
} else {
if (oldT.isStaticPhase) {
//removing "static":
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.STATIC);
copyTo(localPointer, tokenSequence.offset());
} else {
copyTo(localPointer, nameBounds[0]);
printer.print("static "); //NOI18N
}
}
break;
}
} else {
copyTo(localPointer, nameBounds[0]);
if (newT.isStaticPhase) {
printer.print("static "); //NOI18N
}
if (newT.isTransitive) {
printer.print("transitive "); //NOI18N
}
}
localPointer = diffTree(oldT.moduleName, newT.moduleName, nameBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffExports(JCExports oldT, JCExports newT, int[] bounds) {
int localPointer = bounds[0];
// package name
int[] expNameBounds = getBounds(oldT.qualid);
copyTo(localPointer, expNameBounds[0]);
localPointer = diffTree(oldT.qualid, newT.qualid, expNameBounds);
// modules
int posHint;
if (oldT.moduleNames == null || oldT.moduleNames.isEmpty()) {
posHint = endPos(oldT) - 1;
} else {
posHint = oldT.moduleNames.iterator().next().getStartPosition();
}
if (newT.moduleNames != null && !newT.moduleNames.isEmpty()) //do not copy the "to" keyword:
copyTo(localPointer, localPointer = posHint);
PositionEstimator est = EstimatorFactory.exportsOpensTo(oldT.moduleNames, newT.moduleNames, diffContext);
localPointer = diffList2(oldT.moduleNames, newT.moduleNames, posHint, est);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffOpens(JCOpens oldT, JCOpens newT, int[] bounds) {
int localPointer = bounds[0];
// package name
int[] expNameBounds = getBounds(oldT.qualid);
copyTo(localPointer, expNameBounds[0]);
localPointer = diffTree(oldT.qualid, newT.qualid, expNameBounds);
// modules
int posHint;
if (oldT.moduleNames == null || oldT.moduleNames.isEmpty()) {
posHint = endPos(oldT) - 1;
} else {
posHint = oldT.moduleNames.iterator().next().getStartPosition();
}
if (newT.moduleNames != null && !newT.moduleNames.isEmpty()) //do not copy the "to" keyword:
copyTo(localPointer, localPointer = posHint);
PositionEstimator est = EstimatorFactory.exportsOpensTo(oldT.moduleNames, newT.moduleNames, diffContext);
localPointer = diffList2(oldT.moduleNames, newT.moduleNames, posHint, est);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffProvides(JCProvides oldT, JCProvides newT, int[] bounds) {
int localPointer = bounds[0];
// service
int[] servBounds = getBounds(oldT.serviceName);
copyTo(localPointer, servBounds[0]);
localPointer = diffTree(oldT.serviceName, newT.serviceName, servBounds);
// implementations
int posHint;
if (oldT.implNames == null || oldT.implNames.isEmpty()) {
posHint = endPos(oldT) - 1;
} else {
posHint = oldT.implNames.iterator().next().getStartPosition();
}
if (newT.implNames != null && !newT.implNames.isEmpty()) //do not copy the "with" keyword:
copyTo(localPointer, localPointer = posHint);
PositionEstimator est = EstimatorFactory.providesWith(oldT.implNames, newT.implNames, diffContext);
localPointer = diffList2(oldT.implNames, newT.implNames, posHint, est);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffUses(JCUses oldT, JCUses newT, int[] bounds) {
int localPointer = bounds[0];
// service
int[] servBounds = getBounds(oldT.qualid);
copyTo(localPointer, servBounds[0]);
localPointer = diffTree(oldT.qualid, newT.qualid, servBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffPackage(JCPackageDecl oldT, JCPackageDecl newT, int start) {
int packageKeywordStart = start;
JCExpression oldTPid = oldT != null ? oldT.pid : null;
JCExpression newTPid = newT != null ? newT.pid : null;
if (oldTPid != null) {
tokenSequence.move(oldTPid.getStartPosition());
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
packageKeywordStart = tokenSequence.offset();
}
com.sun.tools.javac.util.List<JCAnnotation> oldTAnnotations = oldT != null ? oldT.annotations : com.sun.tools.javac.util.List.<JCAnnotation>nil();
com.sun.tools.javac.util.List<JCAnnotation> newTAnnotations = newT != null ? newT.annotations : com.sun.tools.javac.util.List.<JCAnnotation>nil();
//when adding first annotation, skip initial comments (typically a license):
int localPointer = oldTAnnotations.isEmpty() && !newTAnnotations.isEmpty() ? packageKeywordStart : start;
localPointer = diffAnnotationsLists(oldTAnnotations, newTAnnotations, localPointer, start);
ChangeKind change = getChangeKind(oldTPid, newTPid);
switch (change) {
// packages are the same or not available, i.e. both are null
case NOCHANGE:
if (oldTPid != null) {
localPointer = copyUpTo(localPointer, endPos(oldTPid));
}
break;
// package statement is new, print the keyword and semicolon
case INSERT:
printer.printPackage(newTPid);
break;
// package statement was deleted.
case DELETE:
copyTo(localPointer, packageKeywordStart);
tokenSequence.move(endPos(oldTPid));
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
localPointer = tokenSequence.offset() + 1;
// todo (#pf): check the declaration:
// package org.netbeans /* aa */;
break;
// package statement was modified.
case MODIFY:
copyTo(localPointer, getOldPos(oldTPid));
localPointer = endPos(oldTPid);
printer.print(newTPid);
diffInfo.put(getOldPos(oldTPid), NbBundle.getMessage(CasualDiff.class,"TXT_UpdatePackageStatement"));
break;
}
return localPointer;
}
protected int diffImport(JCImport oldT, JCImport newT, int[] bounds) {
int localPointer = bounds[0];
int[] qualBounds = getBounds(oldT.getQualifiedIdentifier());
if (oldT.staticImport == newT.staticImport) {
copyTo(localPointer, qualBounds[0]);
} else {
if (oldT.staticImport) {
//removing "static":
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.STATIC);
copyTo(localPointer, tokenSequence.offset());
} else {
copyTo(localPointer, qualBounds[0]);
printer.print("static ");
}
}
localPointer = diffTree(oldT.getQualifiedIdentifier(), newT.getQualifiedIdentifier(), qualBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
// TODO: should be here printer.enclClassName be used?
private Name origClassName = null;
private Name newClassName = null;
protected int diffClassDef(JCClassDecl oldT, JCClassDecl newT, int[] bounds) {
int localPointer = bounds[0];
final Name origOuterClassName = origClassName;
final Name newOuterClassName = newClassName;
int insertHint = localPointer;
List<JCTree> filteredOldTDefs = filterHidden(oldT.defs);
List<JCTree> filteredNewTDefs = filterHidden(newT.defs);
// skip the section when printing anonymous class
if (anonClass == false) {
tokenSequence.move(oldT.pos);
tokenSequence.moveNext(); // First skip as move() does not position to token directly
tokenSequence.moveNext();
int afterKindHint = tokenSequence.offset();
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
insertHint = tokenSequence.offset();
localPointer = diffModifiers(oldT.mods, newT.mods, oldT, localPointer);
if (kindChanged(oldT.mods.flags, newT.mods.flags)) {
int pos = oldT.pos;
if ((oldT.mods.flags & Flags.ANNOTATION) != 0) {
tokenSequence.move(pos);
tokenSequence.moveNext();
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
pos = tokenSequence.offset();
}
if ((newT.mods.flags & Flags.ANNOTATION) != 0) {
copyTo(localPointer, pos);
printer.print("@interface"); //NOI18N
} else if ((newT.mods.flags & Flags.ENUM) != 0) {
copyTo(localPointer, pos);
printer.print("enum"); //NOI18N
} else if ((newT.mods.flags & Flags.INTERFACE) != 0) {
copyTo(localPointer, pos);
printer.print("interface"); //NOI18N
} else {
copyTo(localPointer, pos);
printer.print("class"); //NOI18N
}
localPointer = afterKindHint;
}
if (nameChanged(oldT.name, newT.name)) {
copyTo(localPointer, insertHint);
printer.print(newT.name);
diffInfo.put(insertHint, NbBundle.getMessage(CasualDiff.class,"TXT_ChangeClassName"));
localPointer = insertHint += oldT.name.length();
} else {
insertHint += oldT.name.length();
copyTo(localPointer, localPointer = insertHint);
}
origClassName = oldT.name;
newClassName = newT.name;
if (oldT.typarams.nonEmpty() && newT.typarams.nonEmpty()) {
copyTo(localPointer, localPointer = oldT.typarams.head.pos);
}
boolean parens = oldT.typarams.isEmpty() && newT.typarams.nonEmpty();
localPointer = diffParameterList(oldT.typarams, newT.typarams,
parens ? new JavaTokenId[] { JavaTokenId.LT, JavaTokenId.GT } : null,
localPointer, Measure.ARGUMENT);
if (oldT.typarams.nonEmpty()) {
// if type parameters exists, compute correct end of type parameters.
// ! specifies the offset for insertHint var.
// public class Yerba<E, M>! { ...
insertHint = endPos(oldT.typarams.last());
tokenSequence.move(insertHint);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
// it can be > (GT) or >> (SHIFT)
insertHint = tokenSequence.offset() + tokenSequence.token().length();
}
switch (getChangeKind(oldT.extending, newT.extending)) {
case NOCHANGE:
insertHint = oldT.extending != null ? endPos(oldT.extending) : insertHint;
copyTo(localPointer, localPointer = insertHint);
break;
case MODIFY:
copyTo(localPointer, getOldPos(oldT.extending));
localPointer = diffTree(oldT.extending, newT.extending, getBounds(oldT.extending));
break;
case INSERT:
copyTo(localPointer, insertHint);
printer.print(" extends ");
printer.print(newT.extending);
localPointer = insertHint;
break;
case DELETE:
copyTo(localPointer, insertHint);
localPointer = endPos(oldT.extending);
break;
}
// TODO (#pf): there is some space for optimization. If the new list
// is also empty, we can skip this computation.
if (oldT.implementing.isEmpty()) {
// if there is not any implementing part, we need to adjust position
// from different place. Look at the examples in all if branches.
// | represents current adjustment and ! where we want to point to
if (oldT.extending != null)
// public class Yerba<E>| extends Object! { ...
insertHint = endPos(oldT.extending);
else {
// currently no need to adjust anything here:
// public class Yerba<E>|! { ...
}
} else {
// we already have any implements, adjust position to first
// public class Yerba<E>| implements !Mate { ...
// Note: in case of all implements classes are removed,
// diffing mechanism will solve the implements keyword.
insertHint = oldT.implementing.iterator().next().getStartPosition();
}
long flags = oldT.sym != null ? oldT.sym.flags() : oldT.mods.flags;
PositionEstimator estimator = (flags & INTERFACE) == 0 ?
EstimatorFactory.implementz(oldT.getImplementsClause(), newT.getImplementsClause(), diffContext) :
EstimatorFactory.extendz(oldT.getImplementsClause(), newT.getImplementsClause(), diffContext);
if (!newT.implementing.isEmpty())
copyTo(localPointer, insertHint);
localPointer = diffList2(oldT.implementing, newT.implementing, insertHint, estimator);
insertHint = endPos(oldT) - 1;
if (filteredOldTDefs.isEmpty()) {
// if there is nothing in class declaration, use position
// before the closing curly.
insertHint = endPos(oldT) - 1;
} else {
insertHint = filteredOldTDefs.get(0).getStartPosition()-1;
}
tokenSequence.move(insertHint);
tokenSequence.moveNext();
insertHint = moveBackToToken(tokenSequence, insertHint, JavaTokenId.LBRACE) + 1;
} else {
insertHint = moveFwdToToken(tokenSequence, oldT.getKind() == Kind.ENUM ? localPointer : getOldPos(oldT), JavaTokenId.LBRACE);
tokenSequence.moveNext();
insertHint = tokenSequence.offset();
}
int old = printer.indent();
JCClassDecl origClass = printer.enclClass;
printer.enclClass = newT;
PositionEstimator est = EstimatorFactory.members(filteredOldTDefs, filteredNewTDefs, diffContext);
localPointer = copyUpTo(localPointer, insertHint);
// diff inner comments
insertHint = diffInnerComments(oldT, newT, insertHint);
if ((newT.mods.flags & Flags.ENUM) != 0 && !filteredNewTDefs.isEmpty()) {
// also cover the case when the last const is removed for some reason, the semicolon should auto appear
boolean constMissing = filteredOldTDefs.isEmpty();
boolean firstDiff = false;
boolean oldEnum = constMissing;
boolean xxx = (newT.mods.flags & Flags.ENUM) != 0 && filteredOldTDefs.isEmpty() && !filteredNewTDefs.isEmpty() && !isEnum(filteredNewTDefs.get(0)) && !newT.getSimpleName().isEmpty();
if (constMissing) {
firstDiff = !isEnum(filteredNewTDefs.get(0));
} else {
firstDiff = (oldEnum = isEnum(filteredOldTDefs.get(0))) != isEnum(filteredNewTDefs.get(0));
}
if (firstDiff && !newT.getSimpleName().isEmpty()) {
if (oldEnum) {
printer.blankline();
printer.toLeftMargin();
printer.print(";"); //NOI18N
printer.newline();
} else {
// there's probably a semicolon before the 1st member, which must be removed before the 1st constant
// is created.
insertHint = removeExtraEnumSemicolon(insertHint);
}
}
}
localPointer = diffList(filteredOldTDefs, filteredNewTDefs, insertHint, est, Measure.REAL_MEMBER, printer);
printer.enclClass = origClass;
origClassName = origOuterClassName;
newClassName = newOuterClassName;
printer.undent(old);
if (localPointer != -1 && localPointer < origText.length()) {
if (origText.charAt(localPointer) == '}') {
// another stupid hack
printer.toLeftMargin();
}
copyTo(localPointer, bounds[1]);
}
return bounds[1];
}
/**
* When the enumeration contains just methods, it is necessary to preced them with single ;. If a constant is
* inserted, it must be inserted first; and the semicolon should be removed. This method will attempt to remove entire
* lines of whitespace around the semicolon. Preceding or following comments are preserved.
*
* @param insertHint the local Pointer value
* @return new localPointer value
*/
private int removeExtraEnumSemicolon(int insertHint) {
int startWS = -1;
int rewind = tokenSequence.offset();
tokenSequence.move(insertHint);
tokenSequence.moveNext();
boolean semi = false;
out: do {
Token<JavaTokenId> t = tokenSequence.token();
switch (t.id()) {
case WHITESPACE:
if (semi) {
// after semicolon, find the last newline
int nl = t.text().toString().lastIndexOf('\n');
if (nl == -1) {
startWS = tokenSequence.offset();
} else {
startWS = tokenSequence.offset() + nl + 1;
}
} else {
// before semicolon, select the 1st complete line.
if (startWS == -1) {
startWS = t.text().toString().indexOf('\n');
if (startWS == -1) {
startWS = tokenSequence.offset();
} else {
startWS += tokenSequence.offset() + 1;
}
}
}
break;
case SEMICOLON:
if (startWS >= 0) {
// copy up to the WS immediately preceding the semicolon.
copyTo(insertHint, startWS);
insertHint = tokenSequence.offset() + t.length();
}
startWS = -1;
semi = true;
break;
case LINE_COMMENT: case BLOCK_COMMENT: case JAVADOC_COMMENT:
if (semi) {
break out;
}
startWS = -1;
break;
default:
break out;
}
} while (tokenSequence.moveNext());
if (semi && startWS > -1) {
insertHint = startWS;
}
tokenSequence.move(rewind);
tokenSequence.moveNext();
return insertHint;
}
private boolean isEnum(Tree tree) {
if (tree instanceof FieldGroupTree) return ((FieldGroupTree) tree).isEnum();
if (tree instanceof VariableTree) return (((JCVariableDecl) tree).getModifiers().flags & Flags.ENUM) != 0;
if (tree instanceof ClassTree) return (((JCClassDecl) tree).getModifiers().flags & Flags.ENUM) != 0;
return false;
}
private boolean hasModifiers(JCModifiers mods) {
return mods != null && (!mods.getFlags().isEmpty() || !mods.getAnnotations().isEmpty());
}
protected int diffMethodDef(JCMethodDecl oldT, JCMethodDecl newT, int[] bounds) {
int localPointer = bounds[0];
// match modifiers and annotations
if (!matchModifiers(oldT.mods, newT.mods)) {
// if new tree has modifiers, print them
if (hasModifiers(newT.mods)) {
localPointer = diffModifiers(oldT.mods, newT.mods, oldT, localPointer);
} else {
// there are no new modifiers, just skip after existing. --
// endPos of modifiers are not usable, we have to remove also
// space. Go to return type in case of method, in case of
// constructor use whole tree start.
int oldPos = getOldPos(oldT.mods);
copyTo(localPointer, oldPos);
if (oldT.typarams.isEmpty()) {
if (oldT.restype != null) {
localPointer = getOldPos(oldT.restype);
} else {
localPointer = oldT.pos;
}
} else {
int firstTyParam = getOldPos(oldT.typarams.head);
tokenSequence.move(firstTyParam);
JavaTokenId id = moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
if (id == JavaTokenId.LT) {
localPointer = tokenSequence.offset();
} else {
// fallback
localPointer = firstTyParam;
}
}
}
}
// compute the position for type parameters - if type param is empty,
// use in case of
// i) method - start position of return type,
// ii) constructor - start position of tree - i.e. first token after
// modifiers.
int pos = oldT.typarams.isEmpty() ?
oldT.restype != null ?
getOldPos(oldT.restype) :
oldT.pos :
getOldPos(oldT.typarams.head);
if (!listsMatch(oldT.typarams, newT.typarams)) {
if (newT.typarams.nonEmpty())
copyTo(localPointer, pos);
else
if (hasModifiers(oldT.mods))
copyTo(localPointer, endPos(oldT.mods));
boolean parens = oldT.typarams.isEmpty() || newT.typarams.isEmpty();
localPointer = diffParameterList(oldT.typarams,
newT.typarams,
parens ? new JavaTokenId[] { JavaTokenId.LT, JavaTokenId.GT } : null,
pos,
Measure.ARGUMENT
);
if (parens && oldT.typarams.isEmpty()) {
printer.print(" "); // print the space after type parameter
}
}
if (oldT.restype != null) { // means constructor, skip return type gen.
int[] restypeBounds = getBounds(oldT.restype);
copyTo(localPointer, restypeBounds[0]);
localPointer = diffTree(oldT.restype, newT.restype, restypeBounds);
if (restypeBounds[1] > localPointer)
copyTo(localPointer, localPointer = restypeBounds[1]);
} else if(oldT.restype == null && newT.restype != null) {
copyTo(localPointer, localPointer = oldT.pos);
printer.print(newT.restype);
printer.print(" "); // print the space after return type
}
int posHint;
if (oldT.typarams.isEmpty()) {
posHint = oldT.restype != null ? oldT.restype.getStartPosition() : oldT.getStartPosition();
} else {
posHint = oldT.typarams.iterator().next().getStartPosition();
}
if ((oldT.name != names.init || origClassName != null) && (newT.name != names.init || newClassName != null)) {
int origLength = (oldT.name == names.init && origClassName != null ? origClassName.length() : oldT.name.length());
if (nameChanged(oldT.name, newT.name)) {
copyTo(localPointer, oldT.pos);
// use orig class name in case of constructor
if (newT.name == names.init && (newClassName != null)) {
printer.print(newClassName);
localPointer = oldT.pos + origLength;
}
else {
printer.print(newT.name);
diffInfo.put(oldT.pos, NbBundle.getMessage(CasualDiff.class,"TXT_RenameMethod",oldT.name));
localPointer = oldT.pos + origLength;
}
} else {
copyTo(localPointer, localPointer = (oldT.pos + origLength));
}
}
if (oldT.params.isEmpty()) {
// compute the position. Find the parameters closing ')', its
// start position is important for us. This is used when
// there was not any parameter in original tree.
int startOffset = oldT.pos;
moveFwdToToken(tokenSequence, startOffset, JavaTokenId.RPAREN);
posHint = tokenSequence.offset();
} else {
// take the position of the first old parameter
posHint = oldT.params.iterator().next().getStartPosition();
}
if (!listsMatch(oldT.params, newT.params)) {
copyTo(localPointer, posHint);
int old = printer.setPrec(TreeInfo.noPrec);
parameterPrint = true;
JCClassDecl oldEnclClass = printer.enclClass;
printer.enclClass = null;
localPointer = diffParameterList(oldT.params, newT.params, null, posHint, Measure.MEMBER);
printer.enclClass = oldEnclClass;
parameterPrint = false;
printer.setPrec(old);
}
//make sure the ')' is printed:
moveFwdToToken(tokenSequence, oldT.params.isEmpty() ? posHint : endPos(oldT.params.last()), JavaTokenId.RPAREN);
tokenSequence.moveNext();
posHint = tokenSequence.offset();
if (localPointer < posHint)
copyTo(localPointer, localPointer = posHint);
// if abstract, hint is before ending semi-colon, otherwise before method body
if (oldT.thrown.isEmpty()) {
if (oldT.body != null) {
tokenSequence.move(oldT.body.pos);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
posHint = tokenSequence.offset();
} else {
if (oldT.defaultValue != null) {
tokenSequence.move(getOldPos(oldT.defaultValue));
while (tokenSequence.movePrevious() && tokenSequence.token().id() != JavaTokenId.DEFAULT)
;
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
posHint = tokenSequence.offset();
} else {
posHint = endPos(oldT) - 1;
}
}
} else {
posHint = oldT.thrown.iterator().next().getStartPosition();
}
if (!newT.thrown.isEmpty()) //do not copy the "throws" keyword:
copyTo(localPointer, localPointer = posHint);
PositionEstimator est = EstimatorFactory.throwz(oldT.getThrows(), newT.getThrows(), diffContext);
localPointer = diffList2(oldT.thrown, newT.thrown, posHint, est);
if (oldT.defaultValue != newT.defaultValue) {
if (oldT.defaultValue == null) {
printer.print(" default ");
printer.print(newT.defaultValue);
} else {
if (newT.defaultValue == null) {
localPointer = endPos(oldT.defaultValue);
} else {
int[] restypeBounds = getBounds(oldT.defaultValue);
copyTo(localPointer, restypeBounds[0]);
localPointer = diffTree(oldT.defaultValue, newT.defaultValue, restypeBounds);
copyTo(localPointer, localPointer = restypeBounds[1]);
}
}
}
if (newT.body == null && oldT.body != null) {
localPointer = endPos(oldT.body);
printer.print(";");
} else {
if (oldT.body != null && newT.body != null) {
int[] bodyBounds = getBounds(oldT.body);
copyTo(localPointer, bodyBounds[0]);
localPointer = diffTree(oldT.body, newT.body, bodyBounds);
} else if (oldT.body == null && newT.body != null) {
tokenSequence.move(localPointer);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
if (tokenSequence.token().id() == JavaTokenId.SEMICOLON) {
localPointer = tokenSequence.offset() + tokenSequence.token().length();
}
if (diffContext.style.spaceBeforeMethodDeclLeftBrace()) {
printer.print(" ");
}
printer.print(newT.body);
}
}
// TODO: Missing implementation - default value matching!
// diffTree(oldT.defaultValue, newT.defaultValue);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffVarDef(JCVariableDecl oldT, JCVariableDecl newT, int pos) {
int localPointer = oldT.pos;
copyTo(pos, localPointer);
if (nameChanged(oldT.name, newT.name)) {
copyTo(localPointer, oldT.pos);
printer.print(newT.name);
diffInfo.put(oldT.pos, NbBundle.getMessage(CasualDiff.class,"TXT_RenameVariable",oldT.name));
localPointer = oldT.pos + oldT.name.length();
}
if (newT.init != null && oldT.init != null) {
copyTo(localPointer, localPointer = getOldPos(oldT.init));
localPointer = diffTree(oldT.init, newT.init, new int[] { localPointer, endPos(oldT.init) });
} else {
if (oldT.init != null && newT.init == null) {
// remove initial value
pos = getOldPos(oldT.init);
tokenSequence.move(pos);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
int to = tokenSequence.offset();
copyTo(localPointer, to);
localPointer = endPos(oldT.init);
}
if (oldT.init == null && newT.init != null) {
int end = endPos(oldT);
tokenSequence.move(end);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
copyTo(localPointer, localPointer = tokenSequence.offset());
printer.printVarInit(newT);
}
}
copyTo(localPointer, localPointer = endPos(oldT));
return localPointer;
}
private int diffVarDef(JCVariableDecl oldT, JCVariableDecl newT, int[] bounds) {
int localPointer = bounds[0];
// check that it is not enum constant. If so, match it in special way
if ((oldT.mods.flags & Flags.ENUM) != 0) {
JCModifiers mods = oldT.getModifiers();
int startPos = mods.pos != Position.NOPOS ? getOldPos(mods) : getOldPos(parent);
localPointer = diffAnnotationsLists(mods.getAnnotations(), newT.getModifiers().getAnnotations(), startPos, localPointer);
if (nameChanged(oldT.name, newT.name)) {
copyTo(localPointer, oldT.pos);
printer.print(newT.name);
diffInfo.put(oldT.pos, NbBundle.getMessage(CasualDiff.class,"TXT_RenameEnumConstant",oldT.name));
localPointer = oldT.pos + oldT.name.length();
}
JCNewClass oldInit = (JCNewClass) oldT.init;
JCNewClass newInit = (JCNewClass) newT.init;
if (oldInit.args.nonEmpty() && newInit.args.nonEmpty()) {
copyTo(localPointer, localPointer = getOldPos(oldInit.args.head));
localPointer = diffParameterList(oldInit.args, newInit.args, null, localPointer, Measure.ARGUMENT);
}
if (oldInit.def != null && newInit.def != null) {
anonClass = true;
int[] defBounds = new int[] { localPointer, endPos(oldInit.def) } ;
localPointer = diffTree(oldInit.def, newInit.def, defBounds);
anonClass = false;
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
if (!matchModifiers(oldT.mods, newT.mods)) {
// if new tree has modifiers, print them
if (hasModifiers(newT.mods)) {
localPointer = diffModifiers(oldT.mods, newT.mods, oldT, localPointer);
} else {
if (hasModifiers(oldT.mods)) {
int oldPos = getOldPos(oldT.mods);
copyTo(localPointer, oldPos);
localPointer = getOldPos(oldT.vartype);
}
}
}
boolean cLikeArray = false, cLikeArrayChange = false;
int addDimensions = 0;
if (diffContext.syntheticTrees.contains(oldT.vartype)) {
if (!diffContext.syntheticTrees.contains(newT.vartype)) {
int varOffset = findVar(localPointer, oldT.pos);
if (varOffset != (-1)) {
copyTo(localPointer, localPointer = varOffset);
localPointer += 3; //"var" length
}
if (!suppressParameterTypes) {
int l = printer.out.length();
printer.print(newT.vartype);
if (l < printer.out.length() && varOffset == (-1)) {
printer.print(" ");
}
}
}
} else {
if (suppressParameterTypes) {
int[] vartypeBounds = getBounds(oldT.vartype);
// skip the old vartype, if present
localPointer = vartypeBounds[1];
} else if (newT.vartype == null) {
throw new UnsupportedOperationException();
} else {
int[] vartypeBounds = getBounds(oldT.vartype);
addDimensions = dimension(newT.vartype, -1);
cLikeArray = vartypeBounds[1] > oldT.pos;
cLikeArrayChange = cLikeArray && dimension(oldT.vartype, oldT.pos) > addDimensions;
/**
* Extracting modifier from oldTree using symbol position and
* modifier positions when oldT.type is error and vartype
* upperbound is not proper.
*/
if (oldT.type.getKind() == TypeKind.ERROR && vartypeBounds[1] == -1) {
// returns -1 if modifiers not present.
int modsUpperBound = getCommentCorrectedEndPos(oldT.mods);
if (modsUpperBound > -1) {
tokenSequence.move(modsUpperBound);
// copying modifiers from oldTree
if (tokenSequence.moveNext()) {
copyTo(localPointer, localPointer = modsUpperBound);
}
}
int offset = localPointer;
JavaTokenId tokenId = null;
tokenSequence.move(localPointer);
//adding back all whitespaces/block-comment/javadoc-comments present in OldTree before variable type token.
while (tokenSequence.moveNext()) {
offset = tokenSequence.offset();
tokenId = tokenSequence.token().id();
if (!((tokenId == JavaTokenId.WHITESPACE || tokenId == JavaTokenId.BLOCK_COMMENT || tokenId == JavaTokenId.JAVADOC_COMMENT) && offset < oldT.sym.pos)) {
break;
}
}
copyTo(localPointer, localPointer = offset);
// Correcting lower/upper bounds for oldT.vartype tree.
vartypeBounds[1] = oldT.sym.pos;
vartypeBounds[0] = offset;
} else {
copyTo(localPointer, vartypeBounds[0]);
}
localPointer = diffTree(oldT.vartype, newT.vartype, vartypeBounds);
/**
* For erroneous variable type sometime diffTree function return
* wrong position. In that scenario only old variable type will
* be replaced with new variable type leaving out succeeding
* comments tokens present(if any). Below code copies successive
* tokens after excluding variable type token which will be the
* first token.
*/
if (oldT.type.getKind() == TypeKind.ERROR && localPointer == -1) {
// moving to variable type token
tokenSequence.move(vartypeBounds[0]);
tokenSequence.moveNext();
//moving to first token after variable type token.
if (tokenSequence.moveNext()) {
// copying tokens from vartype bounds after excluding variable type token.
int offset = tokenSequence.offset();
copyTo(offset, localPointer = vartypeBounds[1]);
}
}
}
}
if (nameChanged(oldT.name, newT.name)) {
boolean isOldError = oldT.name == Names.instance(context).error;
if (!isOldError) {
copyTo(localPointer, oldT.pos);
} else {
printer.print(" ");
}
if (cLikeArray) {
printer.eatChars(1);
for (int i=0; i< addDimensions; i++) {
printer.print("[]"); //NOI18N
}
printer.print(" "); //NOI18N
}
printer.print(newT.name);
diffInfo.put(oldT.pos, NbBundle.getMessage(CasualDiff.class,"TXT_RenameVariable",oldT.name));
if (!isOldError) {
if (cLikeArray) {
int[] clab = getBounds(oldT.vartype);
localPointer = clab[1];
} else {
localPointer = oldT.pos + oldT.name.length();
}
}
} else if (cLikeArrayChange) {
for (int i=0; i< addDimensions; i++) {
printer.print("[]"); //NOI18N
}
printer.print(" "); //NOI18N
printer.print(newT.name);
}
if (newT.init != null && oldT.init != null) {
copyTo(localPointer, localPointer = getCommentCorrectedOldPos(oldT.init));
localPointer = diffTree(oldT.init, newT.init, new int[] { localPointer, endPos(oldT.init) });
} else {
if (oldT.init != null && newT.init == null) {
// remove initial value
int pos = getOldPos(oldT.init);
tokenSequence.move(pos);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
int to = tokenSequence.offset();
copyTo(localPointer, to);
localPointer = endPos(oldT.init);
}
if (oldT.init == null && newT.init != null) {
int end = endPos(oldT);
tokenSequence.move(end);
tokenSequence.moveNext();
if (!JavaTokenId.COMMA.equals(tokenSequence.token().id()) &&
!JavaTokenId.SEMICOLON.equals(tokenSequence.token().id()))
{
tokenSequence.movePrevious();
}
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
printer.printVarInit(newT);
}
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffBlock(JCBlock oldT, JCBlock newT, int[] blockBounds) {
int localPointer = blockBounds[0];
int startPos = getOldPos(oldT);
int endPos = endPos(oldT);
if (oldT.flags != newT.flags) {
copyTo(localPointer, localPointer = startPos);
if ((oldT.flags & STATIC) == 0 && (newT.flags & STATIC) != 0) {
printer.print("static");
if (diffContext.style.spaceBeforeStaticInitLeftBrace()) {
printer.print(" ");
}
} else if ((oldT.flags & STATIC) != 0 && (newT.flags & STATIC) == 0) {
tokenSequence.move(startPos);
if (tokenSequence.moveNext() && tokenSequence.token().id() == JavaTokenId.STATIC) {
localPointer = tokenSequence.offset() + tokenSequence.token().length();
if (tokenSequence.moveNext() && tokenSequence.token().id() == JavaTokenId.WHITESPACE) {
localPointer = tokenSequence.offset() + tokenSequence.token().length();
}
}
}
} else {
copyTo(localPointer, localPointer = oldT.pos + 1);
}
PositionEstimator est = EstimatorFactory.statements(
filterHidden(oldT.stats),
filterHidden(newT.stats),
diffContext
);
int old = printer.indent();
localPointer = diffInnerComments(oldT, newT, localPointer);
JCClassDecl oldEnclosing = printer.enclClass;
printer.enclClass = null;
List<JCTree> oldstats = filterHidden(oldT.stats);
Position.LineMap lm = this.oldTopLevel.getLineMap();
boolean emptyToNonEmptySingleLine = oldT.stats.isEmpty() && !newT.stats.isEmpty() && lm.getLineNumber(startPos) == lm.getLineNumber(endPos);
if (emptyToNonEmptySingleLine) {
printer.newline();
printer.toLeftMargin();
}
localPointer = diffList(oldstats, filterHidden(newT.stats), localPointer, est, Measure.MEMBER, printer);
printer.enclClass = oldEnclosing;
if (emptyToNonEmptySingleLine) {
printer.undent(old);
printer.toLeftMargin();
tokenSequence.move(localPointer);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
localPointer = tokenSequence.offset();
copyTo(localPointer, localPointer = endPos);
} else {
if (localPointer < endPos) {
/*
JCTree tree = oldstats.get(oldstats.size() - 1);
localPointer = adjustLocalPointer(localPointer, comments.getComments(oldT), CommentSet.RelativePosition.INNER);
CommentSet cs = comments.getComments(tree);
localPointer = adjustLocalPointer(localPointer, cs, CommentSet.RelativePosition.INLINE);
localPointer = adjustLocalPointer(localPointer, cs, CommentSet.RelativePosition.TRAILING);
*/
copyTo(localPointer, localPointer = endPos);
}
printer.undent(old);
}
return localPointer;
}
private int adjustLocalPointer(int localPointer, CommentSet cs, CommentSet.RelativePosition position) {
if (cs == null) return localPointer;
List<Comment> cl = cs.getComments(position);
if (!cl.isEmpty()) {
for (Comment comment : cl) {
localPointer = Math.max(comment.endPos(), localPointer);
}
}
return localPointer;
}
private boolean isComment(JavaTokenId tid) {
switch (tid) {
case LINE_COMMENT:
case BLOCK_COMMENT:
case JAVADOC_COMMENT:
return true;
default:
return false;
}
}
private boolean isNoop(JavaTokenId tid) {
switch (tid) {
case LINE_COMMENT:
case BLOCK_COMMENT:
case JAVADOC_COMMENT:
case WHITESPACE:
return true;
default:
return false;
}
}
private int dimension(JCTree t, int afterPos) {
if (t.getKind() != Kind.ARRAY_TYPE) {
return 0;
}
int add;
if (afterPos >= 0) {
final int[] bounds = getBounds(t);
add = afterPos < bounds[1] ? 1 : 0;
} else {
add = 1;
}
return add + dimension (((JCTree.JCArrayTypeTree)t).getType(), afterPos);
}
protected int diffDoLoop(JCDoWhileLoop oldT, JCDoWhileLoop newT, int[] bounds) {
int localPointer = bounds[0];
int[] bodyBounds = new int[] { localPointer, endPos(oldT.body) };
int oldIndent = newT.body.hasTag(Tag.BLOCK) ? -1 : printer.indent();
localPointer = diffTree(oldT.body, newT.body, bodyBounds, oldT.getKind());
if (!newT.body.hasTag(Tag.BLOCK))
printer.undent(oldIndent);
int[] condBounds = getBounds(oldT.cond);
if (oldT.body.getKind() != Kind.BLOCK && newT.body.getKind() == Kind.BLOCK) {
moveBackToToken(tokenSequence, condBounds[0], JavaTokenId.WHILE);
localPointer = tokenSequence.offset();
} else {
copyTo(localPointer, condBounds[0]);
localPointer = diffTree(oldT.cond, newT.cond, condBounds);
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffWhileLoop(JCWhileLoop oldT, JCWhileLoop newT, int[] bounds) {
int localPointer = bounds[0];
// condition
int[] condPos = getBounds(oldT.cond);
copyTo(localPointer, condPos[0]);
localPointer = diffTree(oldT.cond, newT.cond, condPos);
// body
int[] bodyPos = new int[] { localPointer, endPos(oldT.body) };
int oldIndent = newT.body.hasTag(Tag.BLOCK) ? -1 : printer.indent();
localPointer = diffTree(oldT.body, newT.body, bodyPos, oldT.getKind());
if (!newT.body.hasTag(Tag.BLOCK))
printer.undent(oldIndent);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffForLoop(JCForLoop oldT, JCForLoop newT, int[] bounds) {
int localPointer;
// initializer
if (oldT.init.nonEmpty()) {
// there is something in the init section, using start offset
localPointer = getOldPos(oldT.init.head);
} else {
moveFwdToToken(tokenSequence, bounds[0], JavaTokenId.SEMICOLON);
localPointer = tokenSequence.offset();
}
copyTo(bounds[0], localPointer);
if (!listsMatch(oldT.init, newT.init)) {
boolean oldVariable = containsVariable(oldT.init);
boolean newVariable = containsVariable(newT.init);
if (oldVariable ^ newVariable) {
int oldPrec = printer.setPrec(TreeInfo.noPrec);
localPointer = diffParameterList(oldT.init, newT.init, null, localPointer, Measure.ARGUMENT);
printer.setPrec(oldPrec);
} else {
if (oldVariable) {
List<JCVariableDecl> oldInit = NbCollections.checkedListByCopy(oldT.init, JCVariableDecl.class, false);
FieldGroupTree old = new FieldGroupTree(oldInit);
List<JCVariableDecl> newInit = NbCollections.checkedListByCopy(newT.init, JCVariableDecl.class, false);
FieldGroupTree nue = new FieldGroupTree(newInit);
int[] initBounds = getBounds(oldT.init.head);
JCTree last = oldT.init.get(oldT.init.size() - 1);
long endPos = diffContext.trees.getSourcePositions().getEndPosition(oldTopLevel, last);
initBounds[1] = (int) endPos;
localPointer = diffTree(old, nue, initBounds);
} else {
localPointer = diffParameterList(oldT.init, newT.init, null, localPointer, Measure.ARGUMENT);
}
}
}
// condition
if (oldT.cond != null) {
copyTo(localPointer, localPointer = getOldPos(oldT.cond));
localPointer = diffTree(oldT.cond, newT.cond, getBounds(oldT.cond));
} else {
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.SEMICOLON);
copyTo(localPointer, localPointer = tokenSequence.offset());
}
// steps
if (oldT.step.nonEmpty())
copyTo(localPointer, localPointer = getOldPos(oldT.step.head));
else {
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.SEMICOLON);
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
}
localPointer = diffParameterList(oldT.step, newT.step, null, localPointer, Measure.ARGUMENT);
// body
int[] bodyBounds = new int[] { localPointer, endPos(oldT.body) };
int oldIndent = newT.body.hasTag(Tag.BLOCK) ? -1 : printer.indent();
localPointer = diffTree(oldT.body, newT.body, bodyBounds, oldT.getKind());
if (!newT.body.hasTag(Tag.BLOCK))
printer.undent(oldIndent);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
private static boolean containsVariable(List<JCStatement> statements) {
for (JCStatement s : statements) {
if (s.getKind() == Kind.VARIABLE) {
return true;
}
}
return false;
}
protected int diffForeachLoop(JCEnhancedForLoop oldT, JCEnhancedForLoop newT, int[] bounds) {
int localPointer = bounds[0];
// variable
int[] varBounds = getBounds(oldT.var);
copyTo(localPointer, varBounds[0]);
localPointer = diffTree(oldT.var, newT.var, varBounds);
// expression
int[] exprBounds = getBounds(oldT.expr);
copyTo(localPointer, exprBounds[0]);
localPointer = diffTree(oldT.expr, newT.expr, exprBounds);
// body
int[] bodyBounds = new int[] { localPointer, endPos(oldT.body) };
int oldIndent = newT.body.hasTag(Tag.BLOCK) ? -1 : printer.indent();
localPointer = diffTree(oldT.body, newT.body, bodyBounds, oldT.getKind());
if (!newT.body.hasTag(Tag.BLOCK))
printer.undent(oldIndent);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffLabelled(JCLabeledStatement oldT, JCLabeledStatement newT, int[] bounds) {
int localPointer = bounds[0];
if (nameChanged(oldT.label, newT.label)) {
copyTo(localPointer, localPointer = getOldPos(oldT));
printer.print(newT.label);
localPointer += oldT.label.length();
}
int[] bodyBounds = getBounds(oldT.body);
copyTo(localPointer, bodyBounds[0]);
localPointer = diffTree(oldT.body, newT.body, bodyBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffSwitch(JCSwitch oldT, JCSwitch newT, int[] bounds) {
int localPointer = bounds[0];
// rename in switch
int[] selectorBounds = getBounds(oldT.selector);
copyTo(localPointer, selectorBounds[0]);
localPointer = diffTree(oldT.selector, newT.selector, selectorBounds);
tokenSequence.move(selectorBounds[1]);
do { } while (tokenSequence.moveNext() && JavaTokenId.LBRACE != tokenSequence.token().id());
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
PositionEstimator est = EstimatorFactory.cases(oldT.getCases(), newT.getCases(), diffContext);
localPointer = diffList(oldT.cases, newT.cases, localPointer, est, Measure.MEMBER, printer);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffSwitchExpression(Tree oldT, Tree newT, int[] bounds) {
int localPointer = bounds[0];
// rename in switch
int[] selectorBounds = getBounds((JCTree)TreeShims.getExpressions(oldT).get(0));
copyTo(localPointer, selectorBounds[0]);
localPointer = diffTree((JCTree)TreeShims.getExpressions(oldT).get(0), (JCTree)TreeShims.getExpressions(newT).get(0), selectorBounds);
tokenSequence.move(selectorBounds[1]);
do { } while (tokenSequence.moveNext() && JavaTokenId.LBRACE != tokenSequence.token().id());
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
PositionEstimator est = EstimatorFactory.cases(TreeShims.getCases(oldT), TreeShims.getCases(newT), diffContext);
ListBuffer<JCTree.JCCase> oldTcases = new ListBuffer<JCTree.JCCase>();
for (CaseTree t : TreeShims.getCases(oldT))
oldTcases.append((JCTree.JCCase)t);
ListBuffer<JCTree.JCCase> newTcases = new ListBuffer<JCTree.JCCase>();
for (CaseTree t : TreeShims.getCases(newT))
newTcases.append((JCTree.JCCase)t);
localPointer = diffList(oldTcases.toList(), newTcases.toList(), localPointer, est, Measure.MEMBER, printer);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffCase(JCCase oldT, JCCase newT, int[] bounds) {
int localPointer = bounds[0];
List<JCExpression> oldPatterns = getCasePatterns(oldT);
List<JCExpression> newPatterns = getCasePatterns(newT);
PositionEstimator patternEst = EstimatorFactory.casePatterns(
oldPatterns,
newPatterns,
diffContext
);
int posHint;
int endpos;
int copyTo;
if (oldPatterns.isEmpty()) {
moveFwdToOneOfTokens(tokenSequence, bounds[0], EnumSet.of(JavaTokenId.DEFAULT));
tokenSequence.moveNext();
copyTo = endpos = posHint = tokenSequence.offset();
if (!newPatterns.isEmpty()) {
copyTo = getOldPos(oldT);
}
} else {
copyTo = posHint = oldPatterns.iterator().next().getStartPosition();
endpos = endPos(oldPatterns.get(oldPatterns.size() - 1));
if (newPatterns.isEmpty()) {
localPointer = copyTo = posHint = endpos;
printer.print("default");
}
}
copyTo(localPointer, copyTo);
localPointer = diffList2(oldPatterns, newPatterns, posHint, patternEst);
tokenSequence.move(endpos);
do { } while (tokenSequence.moveNext() && JavaTokenId.COLON != tokenSequence.token().id() && JavaTokenId.ARROW != tokenSequence.token().id());
boolean reindentStatements = false;
if (Objects.equals(getCaseKind(oldT), getCaseKind(newT))) {
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
} else {
if (JavaTokenId.COLON == tokenSequence.token().id()) {
//: => ->
printer.out.needSpace();
printer.print("-> ");
moveToDifferentThan(tokenSequence, Direction.FORWARD, EnumSet.of(JavaTokenId.WHITESPACE));
localPointer = tokenSequence.offset();
} else if (JavaTokenId.ARROW == tokenSequence.token().id()) {
//-> => :
printer.print(":");
if (newT.stats.size() > 1) {
printer.newline();
} else {
printer.out.needSpace();
}
tokenSequence.moveNext();
moveToDifferentThan(tokenSequence, Direction.FORWARD, EnumSet.of(JavaTokenId.WHITESPACE));
localPointer = tokenSequence.offset();
reindentStatements = true;
}
}
PositionEstimator est = EstimatorFactory.statements(
filterHidden(oldT.stats),
filterHidden(newT.stats),
diffContext
);
int old = printer.indent();
SortedSet<int[]> oldReindentRegions = printer.reindentRegions;
printer.reindentRegions = new TreeSet<>(new Comparator<int[]>() { //XXX: copied from the initializer!!
@Override public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int reindentStart = localPointer;
localPointer = diffInnerComments(oldT, newT, localPointer);
JCClassDecl oldEnclosing = printer.enclClass;
printer.enclClass = null;
localPointer = diffList(filterHidden(oldT.stats), filterHidden(newT.stats), localPointer, est, Measure.MEMBER, printer);
printer.enclClass = oldEnclosing;
if (localPointer < endPos(oldT)) {
copyTo(localPointer, localPointer = endPos(oldT));
}
if (reindentStatements) {
printer.reindentRegions = oldReindentRegions;
printer.reindentRegions.add(new int[] {reindentStart, localPointer});
} else {
oldReindentRegions.addAll(printer.reindentRegions);
printer.reindentRegions = oldReindentRegions;
}
printer.undent(old);
return localPointer;
}
public static List<JCExpression> getCasePatterns(JCCase cs) {
try {
return (List<JCExpression>) CaseTree.class.getDeclaredMethod("getExpressions").invoke(cs);
} catch (Throwable t) {
JCExpression pat = cs.getExpression();
return pat != null ? Collections.singletonList(pat) : Collections.emptyList();
}
}
public static Object getCaseKind(JCCase cs) {
try {
return CaseTree.class.getDeclaredMethod("getCaseKind").invoke(cs);
} catch (Throwable t) {
return null;
}
}
protected int diffSynchronized(JCSynchronized oldT, JCSynchronized newT, int[] bounds) {
int localPointer = bounds[0];
// lock
int[] lockBounds = getBounds(oldT.lock);
copyTo(localPointer, lockBounds[0]);
localPointer = diffTree(oldT.lock, newT.lock, lockBounds);
// body
int[] bodyBounds = getBounds(oldT.body);
copyTo(localPointer, bodyBounds[0]);
int oldIndent = newT.body.hasTag(Tag.BLOCK) ? -1 : printer.indent();
localPointer = diffTree(oldT.body, newT.body, bodyBounds);
if (!newT.body.hasTag(Tag.BLOCK))
printer.undent(oldIndent);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffTry(JCTry oldT, JCTry newT, int[] bounds) {
int localPointer = bounds[0];
int[] bodyPos = getBounds(oldT.body);
if (!listsMatch(oldT.resources, newT.resources)) {
if (oldT.resources.nonEmpty() && newT.resources.isEmpty()) {
tokenSequence.move(getOldPos(oldT.resources.head));
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
assert tokenSequence.token().id() == JavaTokenId.LPAREN;
copyTo(localPointer, tokenSequence.offset());
localPointer = bodyPos[0];
} else {
int pos = oldT.resources.isEmpty() ? pos = bodyPos[0] : getOldPos(oldT.resources.head);
copyTo(localPointer, pos);
boolean parens = oldT.resources.isEmpty() || newT.resources.isEmpty();
int oldPrec = printer.setPrec(TreeInfo.noPrec);
if (newT.resources.nonEmpty()) {
//Remove all stms from oldTrees to force it to be reprinted by VeryPretty
com.sun.tools.javac.util.List<JCTree> l = newT.resources;
for (Tree t = l.head; t!= null; l = l.tail, t = l.head) {
printer.oldTrees.remove(t);
}
}
localPointer = diffParameterList(oldT.resources,
newT.resources,
null,
parens ? new JavaTokenId[] { JavaTokenId.LPAREN, JavaTokenId.RPAREN } : null,
pos,
Measure.ARGUMENT,
diffContext.style.spaceBeforeSemi(),
diffContext.style.spaceAfterSemi(),
ListType.RESOURCE,
";" //NOI18N
);
printer.setPrec(oldPrec);
if (parens && oldT.resources.isEmpty()) {
printer.print(" "); // print the space after type parameter
}
}
}
copyTo(localPointer, bodyPos[0]);
localPointer = diffTree(oldT.body, newT.body, bodyPos);
copyTo(localPointer, localPointer = bodyPos[1]);
PositionEstimator est = EstimatorFactory.catches(oldT.getCatches(), newT.getCatches(), oldT.finalizer != null, diffContext);
localPointer = diffList(oldT.catchers, newT.catchers, localPointer, est, Measure.DEFAULT, printer);
if (oldT.finalizer != null) {
int[] finalBounds = getBounds(oldT.finalizer);
if (newT.finalizer != null) {
copyTo(localPointer, finalBounds[0]);
localPointer = diffTree(oldT.finalizer, newT.finalizer, finalBounds);
} else {
int endetHier = oldT.catchers.isEmpty() ? Math.max(endPos(oldT.body), localPointer) : endPos(oldT.catchers);
copyTo(localPointer, endetHier);
localPointer = finalBounds[1];
}
copyTo(localPointer, bounds[1]);
} else {
if (newT.finalizer != null) {
int catchEnd = oldT.catchers.isEmpty() ? bounds[1] : endPos(oldT.catchers.reverse().head);
copyTo(localPointer, localPointer = catchEnd);
printer.printFinallyBlock(newT.finalizer);
copyTo(localPointer, bounds[1]);
} else {
copyTo(localPointer, bounds[1]);
}
}
return bounds[1];
}
protected int diffCatch(JCCatch oldT, JCCatch newT, int[] bounds) {
int localPointer = bounds[0];
// param
int[] paramBounds = getBounds(oldT.param);
copyTo(localPointer, paramBounds[0]);
localPointer = diffTree(oldT.param, newT.param, paramBounds);
// body
int[] bodyBounds = getBounds(oldT.body);
copyTo(localPointer, bodyBounds[0]);
localPointer = diffTree(oldT.body, newT.body, bodyBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffConditional(JCConditional oldT, JCConditional newT, int[] bounds) {
int localPointer = bounds[0];
// cond
int[] condBounds = getBounds(oldT.cond);
copyTo(localPointer, condBounds[0]);
localPointer = diffTree(oldT.cond, newT.cond, condBounds);
// true
int[] trueBounds = getBounds(oldT.truepart);
copyTo(localPointer, trueBounds[0]);
localPointer = diffTree(oldT.truepart, newT.truepart, trueBounds);
// false
int[] falseBounds = getBounds(oldT.falsepart);
copyTo(localPointer, falseBounds[0]);
localPointer = diffTree(oldT.falsepart, newT.falsepart, falseBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffIf(JCIf oldT, JCIf newT, int[] bounds) {
int localPointer = bounds[0];
int start = printer.toString().length();
int[] condBounds = getCommentCorrectedBounds(oldT.cond);
copyTo(localPointer, condBounds[0]);
localPointer = diffTree(oldT.cond, newT.cond, null, condBounds);
copyTo(localPointer, localPointer = condBounds[1]);
int[] partBounds = new int[] { localPointer, endPos(oldT.thenpart) };
printer.conditionStartHack = start;
localPointer = diffTree(oldT.thenpart, newT.thenpart, partBounds, oldT.getKind(), newT.elsepart == null);
printer.conditionStartHack = (-1);
if (oldT.elsepart == null && newT.elsepart != null) {
copyTo(localPointer, localPointer = partBounds[1]);
printer.printElse(newT, newT.thenpart.getKind() == Kind.BLOCK);
} else if (oldT.elsepart != null && newT.elsepart == null) {
// remove else part
copyTo(localPointer, partBounds[1]);
copyTo(getBounds(oldT.elsepart)[1], bounds[1]);
return bounds[1];
} else {
if (oldT.elsepart != null) {
if (oldT.thenpart.getKind() != newT.thenpart.getKind() && newT.thenpart.getKind() == Kind.BLOCK) {
tokenSequence.move(localPointer);
moveToDifferentThan(tokenSequence, Direction.FORWARD, EnumSet.of(JavaTokenId.WHITESPACE));
if (localPointer != tokenSequence.offset()) {
if (diffContext.style.spaceBeforeElse()) {
printer.print(" ");
}
}
localPointer = tokenSequence.offset();
}
partBounds = new int[] { localPointer, endPos(oldT.elsepart) };
localPointer = diffTree(oldT.elsepart, newT.elsepart, partBounds, oldT.getKind());
tokenSequence.move(localPointer);
if (tokenSequence.movePrevious() && tokenSequence.token().id() == JavaTokenId.LINE_COMMENT) {
printer.newline();
}
}
}
if (localPointer < bounds[1])
copyTo(localPointer, localPointer = bounds[1]);
return localPointer;
}
protected int diffExec(JCExpressionStatement oldT, JCExpressionStatement newT, int[] bounds) {
int localPointer = bounds[0];
// expr
int[] exprBounds = getBounds(oldT.expr);
copyTo(localPointer, exprBounds[0]);
localPointer = diffTree(oldT.expr, newT.expr, exprBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffBreak(JCBreak oldT, JCBreak newT, int[] bounds) {
final Name oldTLabel = oldT.getLabel();
final Name newTlabel = newT.getLabel();
return printBreakContinueTree(bounds, oldTLabel, newTlabel, oldT);
}
protected int diffContinue(JCContinue oldT, JCContinue newT, int[] bounds) {
final Name oldTLabel = oldT.label;
final Name newTlabel = newT.label;
return printBreakContinueTree(bounds, oldTLabel, newTlabel, oldT);
}
protected int diffReturn(JCReturn oldT, JCReturn newT, int[] bounds) {
int localPointer = bounds[0];
if (oldT.expr != newT.expr) {
if (oldT.expr == null) {
tokenSequence.move(endPos(oldT));
tokenSequence.movePrevious();
copyTo(localPointer, localPointer = tokenSequence.offset());
if (tokenSequence.token().id() == JavaTokenId.SEMICOLON) {
tokenSequence.movePrevious();
}
if (tokenSequence.token().id() != JavaTokenId.WHITESPACE) {
printer.print(" ");
}
printer.print(newT.expr);
} else if (newT.expr == null) {
copyTo(localPointer, localPointer = getOldPos(oldT) + "return".length());
localPointer = endPos(oldT.expr);
} else {
int[] exprBounds = getBounds(oldT.expr);
copyTo(bounds[0], exprBounds[0]);
localPointer = diffTree(oldT.expr, newT.expr, exprBounds);
}
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffThrow(JCThrow oldT, JCThrow newT, int[] bounds) {
int localPointer = bounds[0];
// expr
int[] exprBounds = getBounds(oldT.expr);
copyTo(localPointer, exprBounds[0]);
localPointer = diffTree(oldT.expr, newT.expr, exprBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffAssert(JCAssert oldT, JCAssert newT, int[] bounds) {
int localPointer = bounds[0];
// cond
int[] condBounds = getBounds(oldT.cond);
copyTo(localPointer, condBounds[0]);
localPointer = diffTree(oldT.cond, newT.cond, condBounds);
// detail
if (oldT.detail != newT.detail) {
if (oldT.detail == null) {
copyTo(localPointer, condBounds[1]);
localPointer = condBounds[1];
printer.print(" : ");
printer.print(newT.detail);
} else {
int[] detailBounds = getBounds(oldT.detail);
if (newT.detail == null) {
copyTo(localPointer, condBounds[1]);
localPointer = detailBounds[1];
} else {
copyTo(localPointer, detailBounds[0]);
localPointer = diffTree(oldT.detail, newT.detail, detailBounds);
}
}
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffApply(JCMethodInvocation oldT, JCMethodInvocation newT, int[] bounds) {
int localPointer = bounds[0];
int[] methBounds = getBounds(oldT.meth);
if (Kind.MEMBER_SELECT == oldT.meth.getKind() && oldT.meth.getKind() == newT.meth.getKind()) {
localPointer = diffSelect((JCFieldAccess) oldT.meth, (JCFieldAccess) newT.meth, methBounds, oldT.typeargs, newT.typeargs);
} else if (oldT.typeargs.isEmpty() && newT.typeargs.isEmpty()) {
localPointer = diffTree(oldT.meth, newT.meth, methBounds);
} else {
copyTo(localPointer, methBounds[0]);
printer.printMethodSelect(newT);
localPointer = methBounds[1];
}
if (!listsMatch(oldT.args, newT.args)) {
if (oldT.args.nonEmpty()) {
int startArg1 = getCommentCorrectedOldPos(oldT.args.head);
tokenSequence.move(startArg1);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
} else {
copyTo(localPointer, localPointer = methBounds[1]);
tokenSequence.move(localPointer);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
}
localPointer = diffParameterList(oldT.args, newT.args, null, localPointer, Measure.ARGUMENT);
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
boolean anonClass = false;
protected int diffNewClass(JCNewClass oldT, JCNewClass newT, int[] bounds) {
int localPointer = bounds[0];
if (oldT.encl != null) {
int[] enclBounds = getBounds(oldT.encl);
if (newT.encl == null) {
moveFwdToToken(tokenSequence, enclBounds[1], JavaTokenId.DOT);
tokenSequence.moveNext();
localPointer = tokenSequence.offset();
} else {
localPointer = diffTree(oldT.encl, newT.encl, enclBounds);
}
}
diffParameterList(oldT.typeargs, newT.typeargs, null, localPointer, Measure.ARGUMENT);
if (!enumConstantPrint) {
int[] clazzBounds = getBounds(oldT.clazz);
copyTo(localPointer, clazzBounds[0]);
localPointer = diffTree(oldT.clazz, newT.clazz, clazzBounds);
}
List<JCTree> oldTFilteredArgs = filterHidden(oldT.args);
if (!oldTFilteredArgs.isEmpty()) {
copyTo(localPointer, localPointer = getOldPos(oldTFilteredArgs.get(0)));
} else if (!enumConstantPrint) {
moveFwdToToken(tokenSequence, oldT.pos, JavaTokenId.LPAREN);
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
}
localPointer = diffParameterList(oldTFilteredArgs, newT.args, null, localPointer, Measure.ARGUMENT);
// let diffClassDef() method notified that anonymous class is printed.
if (oldT.def != newT.def) {
if (oldT.def != null && newT.def != null) {
int[] defBounds = getBounds(oldT.def);
// getBounds(oldT.def)[0] (which is getOldPos(oldT.def)) for
// classes and interfaces is the position of the LBRACE of the
// class/interface body. For enums, it is the start position of
// the enum constant, so we need to move forward to the LBRACE.
if (enumConstantPrint) {
// Move to the end of the arguments if there were any before
// looking for the LBRACE so that we don't stop on an LBRACE
// involved in one of the arguments.
if (!oldTFilteredArgs.isEmpty()) {
defBounds[0] = endPos(oldTFilteredArgs);
}
if (defBounds[0] != -1) {
moveFwdToToken(tokenSequence, defBounds[0], JavaTokenId.LBRACE);
defBounds[0] = tokenSequence.offset();
}
}
copyTo(localPointer, defBounds[0]);
anonClass = true;
localPointer = diffTree(oldT.def, newT.def, defBounds);
anonClass = false;
} else if (newT.def == null) {
if (endPos(oldTFilteredArgs) > localPointer) {
copyTo(localPointer, endPos(oldTFilteredArgs));
}
printer.print(")");
localPointer = endPos(oldT.def);
} else {
copyTo(localPointer, localPointer = endPos(oldT));
printer.printNewClassBody(newT);
}
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffNewArray(JCNewArray oldT, JCNewArray newT, int[] bounds) {
int localPointer = bounds[0];
// elemtype
if (newT.elemtype != null) {
if (oldT.elemtype != null) {
int[] elemtypeBounds = getBounds(oldT.elemtype);
copyTo(localPointer, elemtypeBounds[0]);
localPointer = diffTree(oldT.elemtype, newT.elemtype, elemtypeBounds);
}
if (!listsMatch(oldT.dims, newT.dims) && !newT.dims.isEmpty()) {
// solved just for the change, not insert and delete
for (com.sun.tools.javac.util.List<JCExpression> l1 = oldT.dims, l2 = newT.dims;
l1.nonEmpty(); l1 = l1.tail, l2 = l2.tail) {
int[] span = getBounds(l1.head);
copyTo(localPointer, span[0]);
localPointer = diffTree(l1.head, l2.head, span);
}
}
} else if (oldT.elemtype != null) {
//remove new <type><dimensions>
copyTo(localPointer, getOldPos(oldT));
if (oldT.elems != null) {
localPointer = oldT.dims != null && !oldT.dims.isEmpty() ? endPos(oldT.dims) : endPos(oldT.elemtype);
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.LBRACE);
localPointer = tokenSequence.offset();
} else {
localPointer = endPos(oldT);
}
}
if (oldT.elems != null) {
if (oldT.elems.head != null) {
copyTo(localPointer, getOldPos(oldT.elems.head));
localPointer = diffParameterList(oldT.elems, newT.elems, null, getOldPos(oldT.elems.head), Measure.ARGUMENT);
} else if (newT.elems != null && !newT.elems.isEmpty()) {
//empty initializer array, adding the first element to it
//find {:
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.LBRACE);
tokenSequence.moveNext();
copyTo(localPointer, localPointer = tokenSequence.offset());
localPointer = diffParameterList(oldT.elems, newT.elems, null, localPointer, Measure.ARGUMENT);
}
} else if (newT.elems != null && !newT.elems.isEmpty()) {
//empty initializer array, adding the first element to it
//find {:
if (newT.elemtype != null) printer.print("[]");
printer.print("{");
localPointer = diffParameterList(Collections.<JCTree>emptyList(), newT.elems, null, localPointer, Measure.ARGUMENT);
printer.print("}");
moveFwdToToken(tokenSequence, localPointer, JavaTokenId.SEMICOLON);
tokenSequence.moveNext();
localPointer = bounds[1];
// copyTo(localPointer, localPointer = tokenSequence.offset());
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffParens(JCParens oldT, JCParens newT, int[] bounds) {
int localPointer = bounds[0];
copyTo(localPointer, getCommentCorrectedOldPos(oldT.expr));
localPointer = diffTree(oldT.expr, newT.expr, getBounds(oldT.expr));
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffAssign(JCAssign oldT, JCAssign newT, JCTree parent, int[] bounds) {
int localPointer = bounds[0];
// lhs
int[] lhsBounds = getBounds(oldT.lhs);
if (lhsBounds[0] < 0) {
lhsBounds[0] = getOldPos(oldT.rhs);
lhsBounds[1] = -1;
}
copyTo(localPointer, lhsBounds[0]);
localPointer = diffTree(oldT.lhs, newT.lhs, lhsBounds);
int[] rhsBounds = getCommentCorrectedBounds(oldT.rhs);
//#174552: '=' may be missing if this is a synthetic annotation attribute assignment (of attribute name "value"):
if ( oldT.lhs.getKind() == Kind.IDENTIFIER
&& newT.lhs.getKind() == Kind.IDENTIFIER
&& !((JCIdent) oldT.lhs).name.equals(((JCIdent) newT.lhs).name)) {
tokenSequence.move(rhsBounds[0]);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
if (tokenSequence.token().id() != JavaTokenId.EQ) {
boolean spaceAroundAssignOps = (parent.getKind() == Kind.ANNOTATION || parent.getKind() == Kind.TYPE_ANNOTATION) ? diffContext.style.spaceAroundAnnotationValueAssignOps() : diffContext.style.spaceAroundAssignOps();
if (spaceAroundAssignOps)
printer.print(" = ");
else
printer.print("=");
localPointer = lhsBounds[0];
}
}
//#174552 end
// rhs
copyTo(localPointer, rhsBounds[0]);
localPointer = diffTree(oldT.rhs, newT.rhs, rhsBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffAssignop(JCAssignOp oldT, JCAssignOp newT, int[] bounds) {
int localPointer = bounds[0];
// lhs
int[] lhsBounds = getBounds(oldT.lhs);
copyTo(localPointer, lhsBounds[0]);
localPointer = diffTree(oldT.lhs, newT.lhs, lhsBounds);
if (oldT.getTag() != newT.getTag()) { // todo (#pf): operatorName() does not work
copyTo(localPointer, oldT.pos);
printer.print(getAssignementOperator(newT));
localPointer = oldT.pos + getAssignementOperator(oldT).length();
}
// rhs
int[] rhsBounds = getBounds(oldT.rhs);
copyTo(localPointer, rhsBounds[0]);
localPointer = diffTree(oldT.rhs, newT.rhs, rhsBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
String getAssignementOperator(Tree t) {
String name;
switch (t.getKind()) {
case MULTIPLY_ASSIGNMENT: return "*=";
case DIVIDE_ASSIGNMENT: return "/=";
case REMAINDER_ASSIGNMENT: return "%=";
case PLUS_ASSIGNMENT: return "+=";
case MINUS_ASSIGNMENT: return "-=";
case LEFT_SHIFT_ASSIGNMENT: return "<<=";
case RIGHT_SHIFT_ASSIGNMENT: return ">>=";
case AND_ASSIGNMENT: return "&=";
case XOR_ASSIGNMENT: return "^=";
case OR_ASSIGNMENT: return "|=";
case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: return ">>>=";
default:
throw new IllegalArgumentException("Illegal kind " + t.getKind());
}
}
protected int diffUnary(JCUnary oldT, JCUnary newT, int[] bounds) {
int[] argBounds = getBounds(oldT.arg);
boolean newOpOnLeft = newT.getKind() != Kind.POSTFIX_DECREMENT && newT.getKind() != Kind.POSTFIX_INCREMENT;
if (newOpOnLeft) {
if (oldT.getTag() != newT.getTag()) {
printer.print(operatorName(newT.getTag()));
} else {
copyTo(bounds[0], argBounds[0]);
}
}
int localPointer = diffTree(oldT.arg, newT.arg, argBounds);
localPointer = copyUpTo(localPointer, argBounds[1]);
if (!newOpOnLeft) {
if (oldT.getTag() != newT.getTag()) {
printer.print(operatorName(newT.getTag()));
} else {
copyUpTo(localPointer, bounds[1]);
}
}
return bounds[1];
}
protected int diffBinary(JCBinary oldT, JCBinary newT, int[] bounds) {
int localPointer = bounds[0];
int[] lhsBounds = getBounds(oldT.lhs);
copyTo(localPointer, lhsBounds[0]);
localPointer = diffTree(oldT.lhs, newT.lhs, lhsBounds);
if (oldT.getTag() != newT.getTag()) {
copyTo(localPointer, oldT.pos);
printer.print(operatorName(newT.getTag()));
localPointer = oldT.pos + operatorName(oldT.getTag()).length();
}
int[] rhsBounds = getCommentCorrectedBounds(oldT.rhs);
rhsBounds[0] = copyUpTo(localPointer, rhsBounds[0]);
localPointer = diffTree(oldT.rhs, newT.rhs, rhsBounds);
return copyUpTo(localPointer, bounds[1]);
}
private String operatorName(Tag tag) {
// dummy instance, just to access a public method which should be static
return new Pretty(null, false).operatorName(tag);
}
protected int diffTypeCast(JCTypeCast oldT, JCTypeCast newT, int[] bounds) {
int localPointer = bounds[0];
// indexed
int[] clazzBounds = getBounds(oldT.clazz);
copyTo(localPointer, clazzBounds[0]);
localPointer = diffTree(oldT.clazz, newT.clazz, clazzBounds);
// expression
int[] exprBounds = getBounds(oldT.expr);
exprBounds[0] = copyUpTo(localPointer, exprBounds[0]);
localPointer = diffTree(oldT.expr, newT.expr, exprBounds);
localPointer = copyUpTo(localPointer, bounds[1]);
return localPointer;
}
protected int diffTypeTest(JCInstanceOf oldT, JCInstanceOf newT, int[] bounds) {
int localPointer = bounds[0];
// expr
int[] exprBounds = getBounds(oldT.expr);
copyTo(localPointer, exprBounds[0]);
localPointer = diffTree(oldT.expr, newT.expr, exprBounds);
// clazz
JCTree oldPattern = getPattern(oldT);
JCTree newPattern = getPattern(newT);
int[] clazzBounds = getBounds(oldPattern);
clazzBounds[0] = copyUpTo(localPointer, clazzBounds[0]);
localPointer = diffTree(oldPattern, newPattern, clazzBounds);
localPointer = copyUpTo(localPointer, bounds[1]);
return localPointer;
}
public static JCTree getPattern(JCInstanceOf tree) {
try {
Field clazzField = JCInstanceOf.class.getField("clazz");
return (JCTree) clazzField.get(tree);
} catch (Throwable t) {
try {
Field patternField = JCInstanceOf.class.getField("pattern");
return (JCTree) patternField.get(tree);
} catch (Throwable t2) {
return (JCTree) tree.getType();
}
}
}
protected int diffIndexed(JCArrayAccess oldT, JCArrayAccess newT, int[] bounds) {
int localPointer = bounds[0];
// indexed
int[] indexedBounds = getBounds(oldT.indexed);
copyTo(localPointer, indexedBounds[0]);
localPointer = diffTree(oldT.indexed, newT.indexed, indexedBounds);
// index
int[] indexBounds = getBounds(oldT.index);
indexBounds[0] = copyUpTo(localPointer, indexBounds[0]);
localPointer = diffTree(oldT.index, newT.index, indexBounds);
localPointer = copyUpTo(localPointer, bounds[1]);
return localPointer;
}
protected int diffSelect(JCFieldAccess oldT, JCFieldAccess newT,
int[] bounds,
com.sun.tools.javac.util.List<JCExpression> oldTypePar,
com.sun.tools.javac.util.List<JCExpression> newTypePar)
{
int localPointer = bounds[0];
int[] selectedBounds = getBounds(oldT.selected);
copyTo(localPointer, selectedBounds[0]);
localPointer = diffTree(oldT.selected, newT.selected, selectedBounds);
if (oldTypePar != null && newTypePar != null) {
int insertHint;
if (oldTypePar.nonEmpty() && newTypePar.nonEmpty()) {
insertHint = oldTypePar.head.pos;
} else {
tokenSequence.move(selectedBounds[1]);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
tokenSequence.moveNext();
insertHint = tokenSequence.offset();
}
copyTo(localPointer, localPointer = insertHint);
boolean parens = oldTypePar.isEmpty() && newTypePar.nonEmpty();
localPointer = diffParameterList(oldTypePar, newTypePar,
parens ? new JavaTokenId[] { JavaTokenId.LT, JavaTokenId.GT } : null,
localPointer, Measure.ARGUMENT);
if (oldTypePar.nonEmpty()) {
tokenSequence.move(endPos(oldTypePar.last()));
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);//skips > and any subsequent unimportant tokens
int end = tokenSequence.offset();
if (newTypePar.nonEmpty())
copyTo(localPointer, end);
localPointer = end;
}
} else {
tokenSequence.move(selectedBounds[1]);
if (oldT.name != Names.instance(context).error) {
moveToSrcRelevant(tokenSequence, Direction.FORWARD); // go to dot (.)
moveToSrcRelevant(tokenSequence, Direction.FORWARD); // go to oldT.name token
copyTo(localPointer, localPointer = tokenSequence.offset());
}
}
if (nameChanged(oldT.name, newT.name)) {
int[] nameSpan = treeUtilities.findNameSpan(oldT);
printer.print(newT.name);
diffInfo.put(localPointer, NbBundle.getMessage(CasualDiff.class,"TXT_UpdateReferenceTo",oldT.name));
if (nameSpan != null) {
localPointer = nameSpan[1];
} else {
localPointer = localPointer + oldT.name.length();
}
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffMemberReference(JCMemberReference oldT, JCMemberReference newT, int[] bounds) {
int localPointer = bounds[0];
int[] exprBounds = getBounds(oldT.expr);
copyTo(localPointer, exprBounds[0]);
localPointer = diffTree(oldT.expr, newT.expr, exprBounds);
tokenSequence.move(exprBounds[1]);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
if (tokenSequence.token() != null && tokenSequence.token().id() == JavaTokenId.COLONCOLON) {
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
copyTo(localPointer, localPointer = tokenSequence.offset());
}
com.sun.tools.javac.util.List<JCExpression> oldTypePar = oldT.typeargs != null ? oldT.typeargs : com.sun.tools.javac.util.List.<JCExpression>nil();
com.sun.tools.javac.util.List<JCExpression> newTypePar = newT.typeargs != null ? newT.typeargs : com.sun.tools.javac.util.List.<JCExpression>nil();
if (!listsMatch(oldTypePar, newTypePar)) {
int insertHint;
if (oldTypePar.nonEmpty() && newTypePar.nonEmpty()) {
insertHint = oldTypePar.head.pos;
} else {
insertHint = localPointer;
}
copyTo(localPointer, localPointer = insertHint);
boolean parens = oldTypePar.isEmpty() && newTypePar.nonEmpty();
localPointer = diffParameterList(oldTypePar, newTypePar,
parens ? new JavaTokenId[] { JavaTokenId.LT, JavaTokenId.GT } : null,
localPointer, Measure.ARGUMENT);
if (oldTypePar.nonEmpty()) {
tokenSequence.move(endPos(oldTypePar.last()));
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);//skips > and any subsequent unimportant tokens
int end = tokenSequence.offset();
if (newTypePar.nonEmpty())
copyTo(localPointer, end);
localPointer = end;
}
}
if (nameChanged(oldT.name, newT.name)) {
printer.print(newT.name);
diffInfo.put(localPointer, NbBundle.getMessage(CasualDiff.class,"TXT_UpdateReferenceTo",oldT.name));
localPointer = localPointer + oldT.name.length();
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffSelect(JCFieldAccess oldT, JCFieldAccess newT, int[] bounds) {
return diffSelect(oldT, newT, bounds, null, null);
}
protected int diffIdent(JCIdent oldT, JCIdent newT, int[] bounds) {
if (nameChanged(oldT.name, newT.name)) {
copyTo(bounds[0], oldT.pos);
printer.print(newT.name);
diffInfo.put(oldT.pos, NbBundle.getMessage(CasualDiff.class,"TXT_UpdateReferenceTo",oldT.name));
} else {
copyTo(bounds[0], bounds[1]);
}
return bounds[1];
}
protected int diffLiteral(JCLiteral oldT, JCLiteral newT, int[] bounds) {
if (oldT.typetag != newT.typetag
|| (oldT.value != null && !oldT.value.equals(newT.value)) || possibleTextBlock(oldT, newT)) {
int localPointer = bounds[0];
// literal
int[] literalBounds = getBounds(oldT);
copyTo(localPointer, literalBounds[0]);
printer.print(newT);
copyTo(literalBounds[1], bounds[1]);
} else {
copyTo(bounds[0], bounds[1]);
}
return bounds[1];
}
protected int diffTypeIdent(JCPrimitiveTypeTree oldT, JCPrimitiveTypeTree newT, int[] bounds) {
if (oldT.typetag != newT.typetag) {
printer.print(newT);
} else {
copyTo(bounds[0], bounds[1]);
}
return bounds[1];
}
protected int diffTypeArray(JCArrayTypeTree oldT, JCArrayTypeTree newT, int[] bounds) {
int localPointer = bounds[0];
int[] elemtypeBounds = getBounds(oldT.elemtype);
localPointer = diffTree(oldT.elemtype, newT.elemtype, elemtypeBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffTypeApply(JCTypeApply oldT, JCTypeApply newT, int[] bounds) {
int localPointer = bounds[0];
int[] clazzBounds = getBounds(oldT.clazz);
copyTo(localPointer, clazzBounds[0]);
localPointer = diffTree(oldT.clazz, newT.clazz, clazzBounds);
if (!listsMatch(oldT.arguments, newT.arguments)) {
int pos = oldT.arguments.nonEmpty() ? getOldPos(oldT.arguments.head) : endPos(oldT.clazz);
copyTo(localPointer, pos);
boolean printBrace = false;
localPointer = diffParameterList(
oldT.arguments,
newT.arguments,
printBrace ? new JavaTokenId[] { JavaTokenId.LT, JavaTokenId.GT } : null,
pos,
Measure.ARGUMENT
);
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffAnnotatedType(JCAnnotatedType oldT, JCAnnotatedType newT, int[] bounds) {
int localPointer = bounds[0];
if (!listsMatch(oldT.annotations, newT.annotations)) {
int pos = oldT.annotations.nonEmpty() ? getOldPos(oldT.annotations.head) : bounds[0];
copyTo(localPointer, pos);
localPointer = diffParameterList(
oldT.annotations,
newT.annotations,
null,
null,
pos,
Measure.ARGUMENT,
true, //TODO: should read the code style configuration
false,
ListType.NORMAL,
""
);
}
int[] underlyingBounds = getBounds(oldT.underlyingType);
copyTo(localPointer, underlyingBounds[0]);
localPointer = diffTree(oldT.underlyingType, newT.underlyingType, underlyingBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffTypeParameter(JCTypeParameter oldT, JCTypeParameter newT, int[] bounds) {
int localPointer = bounds[0];
copyTo(localPointer, getOldPos(oldT));
if (nameChanged(oldT.name, newT.name)) {
printer.print(newT.name);
localPointer += oldT.name.length();
}
if (!listsMatch(oldT.bounds, newT.bounds)) {
// todo (#pf): match it for rename only, other matching will be
// finished later.
PositionEstimator est = EstimatorFactory.implementz(oldT.getBounds(), newT.getBounds(), diffContext);
int pos = oldT.bounds.nonEmpty() ? getOldPos(oldT.bounds.head) : -1;
if (pos > -1) {
copyTo(localPointer, pos);
localPointer = diffList2(oldT.bounds, newT.bounds, pos, est);
}
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffWildcard(JCWildcard oldT, JCWildcard newT, int[] bounds) {
int localPointer = bounds[0];
if (oldT.kind != newT.kind) {
copyTo(localPointer, oldT.pos);
printer.print(newT.kind.toString());
localPointer = oldT.pos + oldT.kind.toString().length();
}
JCTree oldBound = oldT.kind.kind != BoundKind.UNBOUND ? oldT.inner : null;
JCTree newBound = newT.kind.kind != BoundKind.UNBOUND ? newT.inner : null;
if (oldBound == newBound && oldBound == null) return localPointer;
int[] innerBounds = getBounds(oldBound);
copyTo(localPointer, innerBounds[0]);
localPointer = diffTree(oldBound, newBound, innerBounds);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffTypeBoundKind(TypeBoundKind oldT, TypeBoundKind newT, int[] bounds) {
int localPointer = bounds[0];
if (oldT.kind != newT.kind) {
copyTo(localPointer, oldT.pos);
printer.print(newT.kind.toString());
localPointer = oldT.pos + oldT.kind.toString().length();
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffAnnotation(JCAnnotation oldT, JCAnnotation newT, int[] bounds) {
int localPointer = bounds[0];
int[] annotationBounds = getBounds(oldT.annotationType);
copyTo(localPointer, annotationBounds[0]);
localPointer = diffTree(oldT.annotationType, newT.annotationType, annotationBounds);
JavaTokenId[] parens = null;
if (oldT.args.nonEmpty()) {
copyTo(localPointer, localPointer = getOldPos(oldT.args.head));
} else {
// check, if there are already written parenthesis
int endPos = endPos(oldT);
tokenSequence.move(endPos);
tokenSequence.movePrevious();
if (JavaTokenId.RPAREN != tokenSequence.token().id()) {
parens = new JavaTokenId[] { JavaTokenId.LPAREN, JavaTokenId.RPAREN };
} else {
endPos -= 1;
}
copyTo(localPointer, localPointer = endPos);
}
localPointer = diffParameterList(oldT.args, newT.args, oldT, parens, localPointer, Measure.ARGUMENT);
copyTo(localPointer, bounds[1]);
return bounds[1];
}
protected int diffModifiers(JCModifiers oldT, JCModifiers newT, JCTree parent, int localPointer) {
if (oldT == newT) {
// modifiers wasn't changed, return the position lastPrinted.
return localPointer;
}
int startPos = oldT.pos != Position.NOPOS ? getOldPos(oldT) : getOldPos(parent);
int firstAnnotationPos = !oldT.getAnnotations().isEmpty() ? getOldPos(oldT.getAnnotations().head) : -1;
int endOffset = endPos(oldT);
//TODO: cannot currently match intermixed annotations and flags/keywords (#196053)
//but at least handle case where annotations are after the keywords:
if (startPos < firstAnnotationPos) {
//first modifiers, then annotations:
if (oldT.flags != newT.flags) {
copyTo(localPointer, startPos);
printer.printFlags(newT.flags & ~Flags.INTERFACE, oldT.getFlags().isEmpty());
tokenSequence.move(firstAnnotationPos);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
localPointer = tokenSequence.offset();
}
}
localPointer = diffAnnotationsLists(oldT.getAnnotations(), newT.getAnnotations(), startPos, localPointer);
if ((oldT.flags & Flags.ANNOTATION) != 0) {
tokenSequence.move(endOffset);
tokenSequence.movePrevious();
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
endOffset = tokenSequence.offset();
}
if (oldT.flags != newT.flags && !(startPos < firstAnnotationPos)) {
if (localPointer == startPos) {
// no annotation printed, do modifiers print immediately
if ((newT.flags & ~Flags.INTERFACE) != 0) {
printer.printFlags(newT.flags & ~Flags.INTERFACE, oldT.getFlags().isEmpty());
localPointer = endOffset > 0 ? endOffset : localPointer;
} else {
if (endOffset > 0) {
tokenSequence.move(endOffset);
while (tokenSequence.moveNext() && JavaTokenId.WHITESPACE == tokenSequence.token().id()) ;
localPointer = tokenSequence.offset();
}
}
} else {
tokenSequence.move(localPointer);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
copyTo(localPointer, localPointer = tokenSequence.offset());
localPointer = tokenSequence.offset();
if (!oldT.getFlags().isEmpty()) localPointer = endOffset;
printer.printFlags(newT.flags, oldT.getFlags().isEmpty());
}
} else {
if (endOffset > localPointer) {
if(localPointer == startPos) {
printer.toLeftMargin();
}
copyTo(localPointer, localPointer = endOffset);
}
}
return localPointer;
}
private int diffAnnotationsLists(com.sun.tools.javac.util.List<JCAnnotation> oldAnnotations, com.sun.tools.javac.util.List<JCAnnotation> newAnnotations, int startPos, int localPointer) {
int annotationsEnd = oldAnnotations.nonEmpty() ? endPos(oldAnnotations) : localPointer;
if (listsMatch(oldAnnotations, newAnnotations)) {
copyTo(localPointer, localPointer = (annotationsEnd != localPointer ? annotationsEnd : startPos));
} else {
tokenSequence.move(startPos);
if (tokenSequence.movePrevious() && JavaTokenId.WHITESPACE == tokenSequence.token().id()) {
String text = tokenSequence.token().text().toString();
int index = text.lastIndexOf('\n');
startPos = tokenSequence.offset();
if (index > -1) {
startPos += index + 1;
}
if (startPos < localPointer) startPos = localPointer;
}
copyTo(localPointer, startPos);
PositionEstimator est = EstimatorFactory.annotations(oldAnnotations,newAnnotations, diffContext, parameterPrint);
localPointer = diffList(oldAnnotations, newAnnotations, startPos, est, Measure.ARGUMENT, printer);
}
return localPointer;
}
protected void diffLetExpr(LetExpr oldT, LetExpr newT) {
// TODO: perhaps better to throw exception here. Should be never
// called.
}
protected void diffErroneous(JCErroneous oldT, JCErroneous newT, int[] bounds) {
JCTree oldTident = oldT.getErrorTrees().get(0);
JCTree newTident = newT.getErrorTrees().get(0);
if (oldTident.getKind() == Kind.IDENTIFIER && newTident.getKind() == Kind.IDENTIFIER) {
diffIdent((JCIdent) oldTident, (JCIdent) newTident, bounds);
}
}
protected int diffLambda(JCLambda oldT, JCLambda newT, int[] bounds) {
int localPointer = bounds[0];
int posHint;
if (oldT.params.isEmpty()) {
// compute the position. Find the parameters closing ')', its
// start position is important for us. This is used when
// there was not any parameter in original tree.
int startOffset = oldT.pos;
moveFwdToToken(tokenSequence, startOffset, JavaTokenId.RPAREN);
posHint = tokenSequence.offset();
} else {
// take the position of the first old parameter
posHint = oldT.params.iterator().next().getStartPosition();
}
if (!listsMatch(oldT.params, newT.params)) {
copyTo(localPointer, posHint);
int old = printer.setPrec(TreeInfo.noPrec);
parameterPrint = true;
JCClassDecl oldEnclClass = printer.enclClass;
printer.enclClass = null;
suppressParameterTypes = newT.paramKind == JCLambda.ParameterKind.IMPLICIT;
// check, if there are already written parenthesis
JavaTokenId[] parens = null;
if(newT.params.size() > 1) {
JavaTokenId id = moveFwdToOneOfTokens(tokenSequence, oldT.params.isEmpty() ? posHint : endPos(oldT.params.last()), LAMBDA_PARAM_END_TOKENS);
if (id != JavaTokenId.RPAREN) {
parens = new JavaTokenId[] { JavaTokenId.LPAREN, JavaTokenId.RPAREN };
}
}
localPointer = diffParameterList(oldT.params, newT.params, parens, posHint, Measure.MEMBER);
suppressParameterTypes = false;
printer.enclClass = oldEnclClass;
parameterPrint = false;
printer.setPrec(old);
}
//make sure the ')' is printed:
JavaTokenId id = moveFwdToOneOfTokens(tokenSequence, oldT.params.isEmpty() ? posHint : endPos(oldT.params.last()), LAMBDA_PARAM_END_TOKENS);
if (id == JavaTokenId.RPAREN) {
tokenSequence.moveNext();
}
// TODO: if the text is broken so that it ends after the arrow or parens, then what ?
posHint = tokenSequence.offset();
if (localPointer < posHint)
copyTo(localPointer, localPointer = posHint);
if (oldT.body != null && newT.body != null) {
int[] bodyBounds = getCommentCorrectedBounds(oldT.body);
copyTo(localPointer, bodyBounds[0]);
localPointer = diffTree(oldT.body, newT.body, bodyBounds);
}
localPointer = copyUpTo(localPointer, bounds[1]);
return localPointer;
}
private static final EnumSet<JavaTokenId> LAMBDA_PARAM_END_TOKENS = EnumSet.of(JavaTokenId.RPAREN, JavaTokenId.ARROW);
protected int diffFieldGroup(FieldGroupTree oldT, FieldGroupTree newT, int[] bounds) {
if (!listsMatch(oldT.getVariables(), newT.getVariables())) {
int localpointer = getCommentCorrectedOldPos(oldT.getVariables().get(0));
// comments may be already handled
if (bounds[0] < localpointer) {
copyTo(bounds[0], localpointer);
} else {
localpointer = bounds[0];
}
if (oldT.isEnum()) {
int pos = diffParameterList(oldT.getVariables(), newT.getVariables(), oldT, null, localpointer, Measure.ARGUMENT, diffContext.style.spaceBeforeComma(), diffContext.style.spaceAfterComma(), ListType.ENUM, ","); //NOI18N
copyTo(pos, bounds[1]);
return bounds[1];
} else {
int pos = diffVarGroup(oldT.getVariables(), newT.getVariables(), null, localpointer, Measure.GROUP_VAR_MEASURE);
copyTo(pos, bounds[1]);
return bounds[1];
}
} else {
tokenSequence.move(oldT.endPos());
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
tokenSequence.moveNext();
return tokenSequence.offset();
}
}
protected boolean listContains(List<? extends JCTree> list, JCTree tree) {
for (JCTree t : list)
if (treesMatch(t, tree))
return true;
return false;
}
protected boolean treesMatch(JCTree t1, JCTree t2) {
return treesMatch(t1, t2, true);
}
public boolean treesMatch(JCTree t1, JCTree t2, boolean deepMatch) {
if (t1 == t2)
return true;
if (t1 == null || t2 == null)
return false;
if (t1.getTag() != t2.getTag())
return false;
if (!deepMatch)
return true;
// don't use visitor, since we want fast-fail behavior
switch (t1.getTag()) {
case TOPLEVEL:
return ((JCCompilationUnit)t1).sourcefile.equals(((JCCompilationUnit)t2).sourcefile);
case IMPORT:
return matchImport((JCImport)t1, (JCImport)t2);
case CLASSDEF:
return ((JCClassDecl)t1).sym == ((JCClassDecl)t2).sym;
case METHODDEF:
return ((JCMethodDecl)t1).sym == ((JCMethodDecl)t2).sym;
case VARDEF:
return ((JCVariableDecl)t1).sym == ((JCVariableDecl)t2).sym;
case SKIP:
return true;
case BLOCK:
return matchBlock((JCBlock)t1, (JCBlock)t2);
case DOLOOP:
return matchDoLoop((JCDoWhileLoop)t1, (JCDoWhileLoop)t2);
case WHILELOOP:
return matchWhileLoop((JCWhileLoop)t1, (JCWhileLoop)t2);
case FORLOOP:
return matchForLoop((JCForLoop)t1, (JCForLoop)t2);
case FOREACHLOOP:
return matchForeachLoop((JCEnhancedForLoop)t1, (JCEnhancedForLoop)t2);
case LABELLED:
return matchLabelled((JCLabeledStatement)t1, (JCLabeledStatement)t2);
case SWITCH:
return matchSwitch((JCSwitch)t1, (JCSwitch)t2);
case CASE:
return matchCase((JCCase)t1, (JCCase)t2);
case SYNCHRONIZED:
return matchSynchronized((JCSynchronized)t1, (JCSynchronized)t2);
case TRY:
return matchTry((JCTry)t1, (JCTry)t2);
case CATCH:
return matchCatch((JCCatch)t1, (JCCatch)t2);
case CONDEXPR:
return matchConditional((JCConditional)t1, (JCConditional)t2);
case IF:
return matchIf((JCIf)t1, (JCIf)t2);
case EXEC:
return treesMatch(((JCExpressionStatement)t1).expr, ((JCExpressionStatement)t2).expr);
case BREAK:
return matchBreak((JCBreak)t1, (JCBreak)t2);
case CONTINUE:
return matchContinue((JCContinue)t1, (JCContinue)t2);
case RETURN:
return treesMatch(((JCReturn)t1).expr, ((JCReturn)t2).expr);
case THROW:
return treesMatch(((JCThrow)t1).expr, ((JCThrow)t2).expr);
case ASSERT:
return matchAssert((JCAssert)t1, (JCAssert)t2);
case APPLY:
return matchApply((JCMethodInvocation)t1, (JCMethodInvocation)t2);
case NEWCLASS:
// #97501: workaround. Not sure about comparing symbols and their
// copying in ImmutableTreeTranslator, making workaround with
// minimal impact - issue has to be fixed correctly in the future.
if (((JCNewClass)t2).def != null) ((JCNewClass)t2).def.sym = null;
return matchNewClass((JCNewClass)t1, (JCNewClass)t2);
case NEWARRAY:
return matchNewArray((JCNewArray)t1, (JCNewArray)t2);
case PARENS:
return treesMatch(((JCParens)t1).expr, ((JCParens)t2).expr);
case ASSIGN:
return matchAssign((JCAssign)t1, (JCAssign)t2);
case TYPECAST:
return matchTypeCast((JCTypeCast)t1, (JCTypeCast)t2);
case TYPETEST:
return matchTypeTest((JCInstanceOf)t1, (JCInstanceOf)t2);
case INDEXED:
return matchIndexed((JCArrayAccess)t1, (JCArrayAccess)t2);
case SELECT:
return matchSelect((JCFieldAccess) t1, (JCFieldAccess) t2);
case REFERENCE:
return matchReference((JCMemberReference) t1, (JCMemberReference) t2);
case IDENT:
return ((JCIdent)t1).getName().contentEquals(((JCIdent)t2).getName());
case LITERAL:
return matchLiteral((JCLiteral)t1, (JCLiteral)t2);
case TYPEIDENT:
return ((JCPrimitiveTypeTree)t1).typetag == ((JCPrimitiveTypeTree)t2).typetag;
case TYPEARRAY:
return treesMatch(((JCArrayTypeTree)t1).elemtype, ((JCArrayTypeTree)t2).elemtype);
case TYPEAPPLY:
return matchTypeApply((JCTypeApply)t1, (JCTypeApply)t2);
case TYPEPARAMETER:
return matchTypeParameter((JCTypeParameter)t1, (JCTypeParameter)t2);
case WILDCARD:
return matchWildcard((JCWildcard)t1, (JCWildcard)t2);
case TYPEBOUNDKIND:
return ((TypeBoundKind)t1).kind == ((TypeBoundKind)t2).kind;
case ANNOTATION: case TYPE_ANNOTATION:
return matchAnnotation((JCAnnotation)t1, (JCAnnotation)t2);
case LETEXPR:
return matchLetExpr((LetExpr)t1, (LetExpr)t2);
case POS:
case NEG:
case NOT:
case COMPL:
case PREINC:
case PREDEC:
case POSTINC:
case POSTDEC:
case NULLCHK:
return matchUnary((JCUnary)t1, (JCUnary)t2);
case OR:
case AND:
case BITOR:
case BITXOR:
case BITAND:
case EQ:
case NE:
case LT:
case GT:
case LE:
case GE:
case SL:
case SR:
case USR:
case PLUS:
case MINUS:
case MUL:
case DIV:
case MOD:
return matchBinary((JCBinary)t1, (JCBinary)t2);
case BITOR_ASG:
case BITXOR_ASG:
case BITAND_ASG:
case SL_ASG:
case SR_ASG:
case USR_ASG:
case PLUS_ASG:
case MINUS_ASG:
case MUL_ASG:
case DIV_ASG:
case MOD_ASG:
return matchAssignop((JCAssignOp)t1, (JCAssignOp)t2);
case ANNOTATED_TYPE:
return matchAnnotatedType((JCAnnotatedType) t1, (JCAnnotatedType) t2);
case LAMBDA:
return matchLambda((JCLambda)t1, (JCLambda)t2);
case ERRONEOUS: {
// errors match, iff their source texts match
SourcePositions sps = this.diffContext.trees.getSourcePositions();
int a1 = (int)sps.getStartPosition(diffContext.origUnit, t1);
int a2 = (int)sps.getEndPosition(diffContext.origUnit, t1);
int b1 = (int)sps.getStartPosition(diffContext.origUnit, t2);
int b2 = (int)sps.getEndPosition(diffContext.origUnit, t2);
if (a1 == b1 && a2 == b2) {
return true;
}
if (a1 == NOPOS || a2 == NOPOS || b1 == NOPOS || b2 == NOPOS) {
return false;
}
if (a1 == -1 || a2 == -1 || b1 == -1 || b2 == -1) {
return false;
}
String sa = diffContext.origText.substring(a1, a2);
String sb = diffContext.origText.substring(b1, b2);
return sa.equals(sb);
}
default:
String msg = ((com.sun.source.tree.Tree)t1).getKind().toString() +
" " + t1.getClass().getName();
throw new AssertionError(msg);
}
}
private boolean kindChanged(long oldFlags, long newFlags) {
return (oldFlags & (Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION))
!= (newFlags & (Flags.INTERFACE | Flags.ENUM | Flags.ANNOTATION));
}
protected boolean nameChanged(Name oldName, Name newName) {
if (oldName == newName)
return false;
if (oldName == null || newName == null)
return true;
byte[] arr1 = oldName.toUtf();
byte[] arr2 = newName.toUtf();
int len = arr1.length;
if (len != arr2.length)
return true;
for (int i = 0; i < len; i++)
if (arr1[i] != arr2[i])
return true;
return false;
}
protected int diffList2(
List<? extends JCTree> oldList, List<? extends JCTree> newList,
int initialPos, PositionEstimator estimator)
{
if (oldList == newList)
return initialPos;
assert oldList != null && newList != null;
int lastOldPos = initialPos;
ListMatcher<JCTree> matcher = ListMatcher.<JCTree>instance(oldList, newList);
if (!matcher.match()) {
return initialPos;
}
Iterator<? extends JCTree> oldIter = oldList.iterator();
ResultItem<JCTree>[] result = matcher.getTransformedResult();
Separator s = matcher.separatorInstance();
s.compute();
int[][] matrix = estimator.getMatrix();
int testPos = initialPos;
int i = 0;
int newIndex = 0;
boolean firstNewItem = true;
for (int j = 0; j < result.length; j++) {
JCTree oldT;
ResultItem<JCTree> item = result[j];
switch (item.operation) {
case MODIFY: {
// perhaps I shouldn't support this!
tokenSequence.moveIndex(matrix[i][4]);
if (tokenSequence.moveNext()) {
testPos = tokenSequence.offset();
if (JavaTokenId.COMMA == tokenSequence.token().id())
testPos += JavaTokenId.COMMA.fixedText().length();
}
oldT = oldIter.next(); ++i;
if (!firstNewItem)
copyTo(lastOldPos, getOldPos(oldT));
if (treesMatch(oldT, item.element, false)) {
lastOldPos = diffTree(oldT, item.element, getBounds(oldT));
} else {
printer.print(item.element);
lastOldPos = Math.max(testPos, endPos(oldT));
}
firstNewItem = false;
newIndex++;
break;
}
case INSERT: {
String prec = s.head(j) ? estimator.head() : s.prev(j) ? estimator.sep() : null;
String tail = s.next(j) ? estimator.sep() : null;
if (estimator.getIndentString() != null && !estimator.getIndentString().equals(" ")) {
prec += estimator.getIndentString();
}
copyTo(lastOldPos, testPos);
printer.print(prec);
printer.print(item.element);
printer.print(tail);
//append(Diff.insert(testPos, prec, item.element, tail, LineInsertionType.NONE));
firstNewItem = false;
newIndex++;
break;
}
case DELETE: {
// compute offsets for removal (tree bounds are not enough
// in this case, we have to remove also tokens around like
// preceding keyword, i.e. throws, implements etc. and also
// separators like commas.
// this is a hack: be careful when removing the first:
// throws Exception,IOException... do not remove the space
// after throws keyword, there is not space after comma!
int delta = 0;
if (i == 0 && matrix[i+1][2] != -1 && matrix[i+1][2] == matrix[i+1][3]) {
++delta;
}
int startOffset = toOff(s.head(j) || s.prev(j) ? matrix[i][1] : matrix[i][2+delta]);
int endOffset = toOff(s.tail(j) || s.next(j) ? matrix[i+1][2] : matrix[i][4]);
assert startOffset != -1 && endOffset != -1 : "Invalid offset!";
//printer.print(origText.substring(lastOldPos, startOffset));
//append(Diff.delete(startOffset, endOffset));
tokenSequence.moveIndex(matrix[i][4]);
if (tokenSequence.moveNext()) {
testPos = tokenSequence.offset();
if (JavaTokenId.COMMA == tokenSequence.token().id()) {
moveToDifferentThan(tokenSequence, Direction.FORWARD, EnumSet.of(JavaTokenId.WHITESPACE));
testPos = tokenSequence.offset();
}
}
if (i == 0 && !newList.isEmpty()) {
lastOldPos = endOffset;
} else {
lastOldPos = Math.max(testPos, endPos(item.element));
}
oldT = oldIter.next(); ++i;
break;
}
case NOCHANGE: {
tokenSequence.moveIndex(matrix[i][4]);
if (tokenSequence.moveNext()) {
testPos = tokenSequence.offset();
if (JavaTokenId.COMMA == tokenSequence.token().id()) {
moveToDifferentThan(tokenSequence, Direction.FORWARD, EnumSet.of(JavaTokenId.WHITESPACE));
testPos = tokenSequence.offset();
}
}
oldT = oldIter.next(); ++i;
newIndex++;
int copyTo;
if (newIndex < newList.size()) {
copyTo = Math.max(testPos, endPos(oldT));
} else {
copyTo = endPos(oldT);
}
copyTo(lastOldPos, lastOldPos = copyTo);
firstNewItem = false;
break;
}
}
}
return lastOldPos;
}
/**
* Rewrites <code>break</code> or <code>continue</code> tree.
* @param bounds original bounds
* @param oldTLabel old label
* @param newTlabel new label
* @param oldT the tree to be rewritten
* @return new bounds
*/
private int printBreakContinueTree(int[] bounds, final Name oldTLabel, final Name newTlabel, JCStatement oldT) {
int localPointer = bounds[0];
String stmt = oldT.getKind() == Kind.BREAK ? "break" : "continue"; //NOI18N
// PENDING: inner comments should be handled - inner comment should be printed in between break and its label,
// or after the break with no label.
if (nameChanged(oldTLabel, newTlabel)) {
int labelPos = -1;
copyTo(localPointer, localPointer = getOldPos(oldT));
printer.print(stmt);
localPointer += stmt.length();
int commentStart = -1;
int commentEnd = -1;
if (oldTLabel != null && oldTLabel.length() > 0) {
tokenSequence.move(localPointer);
while (tokenSequence.moveNext()) {
Token<JavaTokenId> tukac = tokenSequence.token();
if (isComment(tukac.id())) {
if (commentStart == -1) {
commentStart = tokenSequence.offset();
}
commentEnd = tokenSequence.offset() + tukac.length();
} else if (tukac.id() != JavaTokenId.WHITESPACE) {
break;
}
}
if (commentStart != -1) {
// replicate whitespace before the comment + all the comments up to the last one:
localPointer = copyUpTo(localPointer, commentEnd);
}
// start of the old label
labelPos = tokenSequence.offset();
}
if (newTlabel != null && newTlabel.length() > 0) {
if (oldTLabel != null) {
// replicate the original whitespaces
localPointer = copyUpTo(localPointer, labelPos);
} else {
printer.print(" ");
}
printer.print(newTlabel);
}
if (oldTLabel != null) {
localPointer = labelPos + oldTLabel.length();
}
}
copyTo(localPointer, bounds[1]);
return bounds[1];
}
private int toOff(int tokenIndex) {
if (tokenIndex == -1) {
return -1;
}
tokenSequence.moveIndex(tokenIndex);
tokenSequence.moveNext();
return tokenSequence.offset();
}
/**
* Diff two lists of parameters separated by comma. It is used e.g.
* from type parameters and method parameters.
*
*/
private int diffParameterList(
List<? extends JCTree> oldList,
List<? extends JCTree> newList,
JavaTokenId[] makeAround,
int pos,
Comparator<JCTree> measure)
{
return diffParameterList(oldList, newList, null, makeAround, pos, measure);
}
private int diffParameterList(
List<? extends JCTree> oldList,
List<? extends JCTree> newList,
JCTree parent,
JavaTokenId[] makeAround,
int pos,
Comparator<JCTree> measure)
{
return diffParameterList(oldList, newList, parent, makeAround, pos, measure, diffContext.style.spaceBeforeComma(), diffContext.style.spaceAfterComma(), ListType.NORMAL, ","); //NOI18N
}
private int diffParameterList(
List<? extends JCTree> oldList,
List<? extends JCTree> newList,
JavaTokenId[] makeAround,
int pos,
Comparator<JCTree> measure,
boolean spaceBefore,
boolean spaceAfter,
ListType listType,
String separator)
{
return diffParameterList(oldList, newList, null, makeAround, pos, measure, spaceBefore, spaceAfter, listType, separator);
}
/**
* Suppresses print out of parameter types; used for diff of parameters of an IMPLICIT param kind lambda expression.
*/
private boolean suppressParameterTypes;
private int diffParameterList(
List<? extends JCTree> oldList,
List<? extends JCTree> newList,
JCTree parent,
JavaTokenId[] makeAround,
int pos,
Comparator<JCTree> measure,
boolean spaceBefore,
boolean spaceAfter,
ListType listType,
String separator)
{
assert oldList != null && newList != null;
if (oldList == newList || oldList.equals(newList))
return pos; // they match perfectly or no need to do anything
boolean printParens = makeAround != null && makeAround.length != 0;
if (newList.isEmpty()) {
int endPos = endPos(oldList);
if (printParens) {
tokenSequence.move(endPos);
moveFwdToToken(tokenSequence, endPos, makeAround[1]);
tokenSequence.moveNext();
endPos = tokenSequence.offset();
if (!nonRelevant.contains(tokenSequence.token().id()))
printer.print(" "); // use options, if mods should be at new line
}
return endPos;
}
ListMatcher<JCTree> matcher = ListMatcher.<JCTree>instance(oldList, newList, measure);
if (!matcher.match()) {
// nothing in the list, no need to print and nothing was printed
return pos;
}
ResultItem<JCTree>[] result = matcher.getResult();
if (printParens/* && oldList.isEmpty()*/) {
printer.print(makeAround[0].fixedText());
}
int oldIndex = 0;
boolean wasLeadingDelete = false;
boolean wasComma = false;
for (int j = 0; j < result.length; j++) {
ResultItem<JCTree> item = result[j];
switch (item.operation) {
case MODIFY: {
JCTree tree = oldList.get(oldIndex++);
int[] bounds = getCommentCorrectedBounds(tree);
tokenSequence.move(bounds[0]);
int start = -1;
if (oldIndex != 1 && !separator.isEmpty()) {
if (wasLeadingDelete) {
start = Math.max(offsetToSrcWiteOnLine(tokenSequence, Direction.BACKWARD), pos);
} else {
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
}
}
if (start == -1) {
tokenSequence.moveNext();
start = Math.max(tokenSequence.offset(), pos);
}
// in case when invoked through diffFieldGroup for enums, comments are already handled.
if (start < bounds[0]) {
copyTo(start, bounds[0], printer);
} else {
bounds[0] = Math.max(start, bounds[0]);
}
diffTree(tree, item.element, parent, bounds);
int end;
switch (listType) {
case RESOURCE:
tokenSequence.move(bounds[1]);
tokenSequence.movePrevious();
if (tokenSequence.token().id() == JavaTokenId.SEMICOLON) {
end = tokenSequence.offset();
break;
}
//intentional fall-through:
default:
tokenSequence.move(bounds[1]);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
end = Math.max(tokenSequence.offset(), bounds[1]);
break;
}
if (!commaNeeded(result, item) &&
listType == ListType.ENUM &&
tokenSequence.token().id() == JavaTokenId.RBRACKET) {
printer.print(";");
}
copyTo(bounds[1], pos = end, printer);
wasLeadingDelete = false;
break;
}
// insert new element
case INSERT: {
if (wasComma) {
if (spaceAfter) {
printer.print(" ");
}
}
printer.suppressVariableType = suppressParameterTypes;
printer.print(item.element);
printer.suppressVariableType = false;
wasLeadingDelete = false;
break;
}
case DELETE:
wasLeadingDelete |= oldIndex++ == 0;
int endPos = getBounds(item.element)[1];
tokenSequence.move(endPos);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
if (tokenSequence.token().id() == JavaTokenId.COMMA) {
if (tokenSequence.moveNext()) {
// moveToSrcRelevant(tokenSequence, Direction.FORWARD);
}
}
pos = Math.max(tokenSequence.offset(), endPos);
break;
// just copy existing element
case NOCHANGE:
if (oldIndex++ == 0 && wasComma) {
if (spaceAfter) {
printer.print(" ");
}
}
int[] bounds = getCommentCorrectedBounds(item.element);
tokenSequence.move(bounds[0]);
int start = -1;
if (oldIndex != 1 && !separator.isEmpty()) {
if (wasLeadingDelete) {
start = offsetToSrcWiteOnLine(tokenSequence, Direction.BACKWARD);
} else {
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
}
}
if (start == -1) {
tokenSequence.moveNext();
start = tokenSequence.offset();
}
boolean forceUseOfTokenOffset = false;
switch (listType) {
case RESOURCE:
tokenSequence.move(bounds[1]);
tokenSequence.movePrevious();
if (tokenSequence.token().id() == JavaTokenId.SEMICOLON) {
forceUseOfTokenOffset = true;
break;
}
//intentional fall-through:
default:
tokenSequence.move(bounds[1]);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
break;
}
int end;
if (listType == ListType.ENUM) {
if ((tokenSequence.token().id() == JavaTokenId.SEMICOLON || tokenSequence.token().id() == JavaTokenId.COMMA)) {
end = tokenSequence.offset();
} else {
end = bounds[1];
}
} else if (oldIndex < oldList.size() || forceUseOfTokenOffset) {
end = tokenSequence.offset();
} else {
end = bounds[1];
}
start = Math.max(start, pos);
copyTo(start, pos = end, printer);
wasLeadingDelete = false;
break;
default:
break;
}
if (commaNeeded(result, item)) {
if ((item.operation == Operation.INSERT || (oldIndex == oldList.size() && j + 1 < result.length && result[j + 1].operation == Operation.INSERT)) && spaceBefore) {
printer.print(" ");
}
printer.print(separator);
wasComma = true;
} else {
if (item.operation != Operation.DELETE) {
wasComma = false;
}
// printer.print(";");
}
}
if (printParens/* && oldList.isEmpty()*/) {
printer.print(makeAround[1].fixedText());
}
if (oldList.isEmpty()) {
return pos;
} else {
int endPos2 = endPos(oldList);
tokenSequence.move(endPos2);
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
if (listType == ListType.ENUM &&
(tokenSequence.token().id() == JavaTokenId.SEMICOLON || tokenSequence.token().id() == JavaTokenId.COMMA)) {
return tokenSequence.offset();
}
return pos;
}
}
enum ListType {
NORMAL,
ENUM,
RESOURCE;
}
/**
* Diff two lists of parameters separated by comma. It is used e.g.
* from type parameters and method parameters.
*
*/
private int diffVarGroup(
List<? extends JCTree> oldList,
List<? extends JCTree> newList,
JavaTokenId[] makeAround,
int pos,
Comparator<JCTree> measure)
{
assert oldList != null && newList != null;
if (oldList == newList || oldList.equals(newList))
return pos; // they match perfectly or no need to do anything
boolean printParens = makeAround != null && makeAround.length != 0;
if (newList.isEmpty()) {
int endPos = endPos(oldList);
if (printParens) {
tokenSequence.move(endPos);
moveFwdToToken(tokenSequence, endPos, makeAround[1]);
tokenSequence.moveNext();
endPos = tokenSequence.offset();
if (!nonRelevant.contains(tokenSequence.token().id()))
printer.print(" "); // use options, if mods should be at new line
}
return endPos;
}
ListMatcher<JCTree> matcher = ListMatcher.<JCTree>instance(oldList, newList, measure);
if (!matcher.match()) {
// nothing in the list, no need to print and nothing was printed
return pos;
}
ResultItem<JCTree>[] result = matcher.getResult();
if (printParens && oldList.isEmpty()) {
printer.print(makeAround[0].fixedText());
}
int oldIndex = 0;
boolean skipWhitespaces = false;
for (int j = 0; j < result.length; j++) {
ResultItem<JCTree> item = result[j];
switch (item.operation) {
case MODIFY: {
JCTree tree = oldList.get(oldIndex++);
int[] bounds = getBounds(tree);
if (oldIndex != 1) {
bounds[0] = tree.pos;
}
if(j == 0) {
copyTo(pos, pos = bounds[0]);
}
tokenSequence.move(bounds[1]);
tokenSequence.movePrevious();
if (tokenSequence.token().id() == JavaTokenId.COMMA || tokenSequence.token().id() == JavaTokenId.SEMICOLON) {
bounds[1] = tokenSequence.offset();
}
tokenSequence.move(bounds[0]);
if (oldIndex != 1 && !skipWhitespaces) {
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
}
tokenSequence.moveNext();
int start = tokenSequence.offset();
copyTo(start, start = bounds[0], printer);
if (j > 0) {
// Comments were handled by generic code for j == 0 (leading var)
CommentSet old = comments.getComments(tree);
CommentSet cs = comments.getComments(item.element);
List<Comment> oldPrecedingComments = old.getComments(CommentSet.RelativePosition.PRECEDING);
List<Comment> newPrecedingComments = cs.getComments(CommentSet.RelativePosition.PRECEDING);
int indentReset = -1;
if (oldPrecedingComments.isEmpty() && !newPrecedingComments.isEmpty()) {
if (printer.out.isWhitespaceLine()) {
indentReset = printer.getIndent();
printer.setIndent(printer.out.getCol());
} else {
printer.newline();
printer.toLeftMargin();
}
}
start = diffPrecedingComments(tree, item.element, bounds[0], start, false);
if (indentReset != (-1)) {
printer.setIndent(indentReset);
}
}
int localPointer;
if (oldIndex != 1) {
localPointer = diffVarDef((JCVariableDecl) tree, (JCVariableDecl) item.element, bounds[0]);
} else {
localPointer = diffVarDef((JCVariableDecl) tree, (JCVariableDecl) item.element, bounds);
}
copyTo(localPointer, pos = bounds[1], printer);
skipWhitespaces = false;
break;
}
case INSERT: {
JCVariableDecl decl = (JCVariableDecl) item.element;
if(j == 0) {
JCTree tree = oldList.get(oldIndex);
int[] bounds = getBounds(tree);
copyTo(pos, pos = bounds[0]);
}
if (oldIndex == 0) {
int oldPrec = printer.setPrec(TreeInfo.noPrec);
printer.visitVarDef(decl);
printer.setPrec(oldPrec);
} else {
if (diffContext.style.spaceAfterComma()) {
printer.print(" ");
}
printer.print(decl.name);
if (decl.init != null) {
printer.printVarInit(decl);
}
}
skipWhitespaces = false;
break;
}
// just copy existing element
case NOCHANGE: {
oldIndex++;
int[] bounds = getBounds(item.element);
if (j != 0) {
bounds[0] = item.element.pos;
} else {
copyTo(pos, pos = bounds[0]);
}
tokenSequence.move(bounds[0]);
if (j != 0 && !skipWhitespaces) {
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
}
tokenSequence.moveNext();
int start = tokenSequence.offset();
int end = bounds[1];
tokenSequence.move(end);
tokenSequence.movePrevious();
if (tokenSequence.token().id() == JavaTokenId.COMMA || tokenSequence.token().id() == JavaTokenId.SEMICOLON) {
end = tokenSequence.offset();
}
copyTo(start, pos = end, printer);
skipWhitespaces = false;
break;
}
case DELETE: {
skipWhitespaces = false;
if (j == 0) {
//deleting the very first variable, diff the modifiers and type explicitly:
JCVariableDecl oldEl = (JCVariableDecl) oldList.get(0);
JCVariableDecl newEl = (JCVariableDecl) newList.get(0);
int[] bounds = getBounds(oldEl.getModifiers());
copyTo(pos, bounds[0]);
pos = diffTree(oldEl.getModifiers(), newEl.getModifiers(), bounds);
bounds = getBounds(oldEl.getType());
copyTo(pos, pos = bounds[0]);
pos = diffTree(oldEl.getType(), newEl.getType(), bounds);
copyTo(pos, item.element.pos);
skipWhitespaces = true;
}
int[] bounds = getBounds(item.element);
tokenSequence.move(bounds[1]);
tokenSequence.movePrevious();
if (tokenSequence.token().id() == JavaTokenId.COMMA || tokenSequence.token().id() == JavaTokenId.SEMICOLON) {
bounds[1] = tokenSequence.offset();
}
pos = bounds[1];
break;
}
default:
break;
}
if (commaNeeded(result, item)) {
printer.print(",");
}
}
if (printParens && oldList.isEmpty()) {
printer.print(makeAround[1].fixedText());
}
return pos;
}
protected int diffUnionType(JCTypeUnion oldT, JCTypeUnion newT, int[] bounds) {
int localPointer = bounds[0];
int pos = diffParameterList(oldT.alternatives, newT.alternatives, null, localPointer, Measure.MEMBER, diffContext.style.spaceAroundBinaryOps(), diffContext.style.spaceAroundBinaryOps(), ListType.NORMAL, "|");
return Math.min(pos, bounds[1]);
}
private boolean commaNeeded(ResultItem[] arr, ResultItem item) {
if (item.operation == Operation.DELETE) {
return false;
}
boolean result = false;
for (int i = 0; i < arr.length; i++) {
if (item == arr[i]) {
result = true;
} else if (result && arr[i].operation != Operation.DELETE) {
return true;
}
}
return false;
}
private List<JCTree> filterHidden(List<? extends JCTree> list) {
return filterHidden(diffContext, list);
}
public static List<JCTree> filterHidden(DiffContext diffContext, List<? extends JCTree> list) {
LinkedList<JCTree> result = new LinkedList<>(); // todo (#pf): capacity?
List<JCVariableDecl> fieldGroup = new ArrayList<>();
List<JCVariableDecl> enumConstants = new ArrayList<>();
for (JCTree tree : list) {
if (tree.pos == (-1)) continue;
if (diffContext.syntheticTrees.contains(tree)) continue;
else if (Kind.VARIABLE == tree.getKind()) {
JCVariableDecl var = (JCVariableDecl) tree;
if ((var.mods.flags & Flags.ENUM) != 0) {
// collect enum constants, make a field group from them
// and set the flag.
enumConstants.add(var);
} // filter syntetic member variable, i.e. variable which are in
// the tree, but not available in the source.
else if ((var.mods.flags & GENERATED_MEMBER) != 0)
continue;
else {
if (!fieldGroup.isEmpty()) {
int oldPos = getOldPos(fieldGroup.get(0));
if (oldPos != (-1) && oldPos != NOPOS && oldPos == getOldPos(var) && fieldGroup.get(0).getModifiers() == var.getModifiers() && !isVarTypeVariable(var)) {
//seems like a field group:
fieldGroup.add(var);
} else {
if (fieldGroup.size() > 1) {
result.add(new FieldGroupTree(fieldGroup));
} else {
result.add(fieldGroup.get(0));
}
fieldGroup = new ArrayList<JCVariableDecl>();
fieldGroup.add(var);
}
} else {
fieldGroup.add(var);
}
}
continue;
}
if (!fieldGroup.isEmpty()) {
if (fieldGroup.size() > 1) {
result.add(new FieldGroupTree(fieldGroup));
} else {
result.add(fieldGroup.get(0));
}
fieldGroup = new ArrayList<JCVariableDecl>();
}
if (Kind.METHOD == tree.getKind()) {
// filter syntetic constructors, i.e. constructors which are in
// the tree, but not available in the source.
if (tree.pos == (-1) || (((JCMethodDecl)tree).mods.flags & Flags.GENERATEDCONSTR) != 0)
continue;
} else if (Kind.BLOCK == tree.getKind()) {
JCBlock block = (JCBlock) tree;
if (block.stats.isEmpty() && block.pos == -1 && block.flags == 0)
// I believe this is an sythetic block
continue;
}
result.add(tree);
}
if (!fieldGroup.isEmpty()) {
if (fieldGroup.size() > 1) {
result.add(new FieldGroupTree(fieldGroup));
} else {
result.add(fieldGroup.get(0));
}
}
if (!enumConstants.isEmpty()) {
result.addFirst(new FieldGroupTree(enumConstants, !result.isEmpty()));
}
return result;
}
private int diffList(
List<? extends JCTree> oldList,
List<? extends JCTree> newList,
int localPointer,
PositionEstimator estimator,
Comparator<JCTree> measure,
VeryPretty printer)
{
if (oldList == newList || oldList.equals(newList)) {
return localPointer;
}
assert oldList != null && newList != null;
ListMatcher<JCTree> matcher = ListMatcher.<JCTree>instance(
oldList,
newList,
measure
);
if (!matcher.match()) {
return localPointer;
}
Queue<JCTree> deletedItems = new LinkedList<>(); // deleted items
JCTree lastdel = null; // last deleted element
ResultItem<JCTree>[] result = matcher.getResult();
// if there hasn't been import but at least one is added
if (oldList.isEmpty() && !newList.isEmpty()) {
// such a situation needs special handling. It is difficult to
// obtain a correct position.
StringBuilder aHead = new StringBuilder(), aTail = new StringBuilder();
int pos = estimator.prepare(localPointer, aHead, aTail);
// #248058: do not eat characters if pos < localPointer: diffInnerComments sometimes
// advances localPointer, but does not copy non-comment whitespaces, while pos
// is positioned at the start of preceding whitespaces.
copyUpTo(localPointer, pos, printer);
if (newList.get(0).getKind() == Kind.IMPORT) {
printer.printImportsBlock(newList, true);
} else {
printer.print(aHead.toString());
for (JCTree item : newList) {
if (LineInsertionType.BEFORE == estimator.lineInsertType()) printer.newline();
printer.print(item);
if (LineInsertionType.AFTER == estimator.lineInsertType()) printer.newline();
}
printer.print(aTail.toString());
}
return pos;
}
// if there has been imports which is removed now
if (newList.isEmpty() && !oldList.isEmpty()) {
int[] removalBounds = estimator.sectionRemovalBounds(null);
copyTo(localPointer, removalBounds[0]);
return removalBounds[1];
}
CodeStyle.ImportGroups importGroups = newList.get(0).getKind() == Kind.IMPORT && diffContext.style.separateImportGroups()
? diffContext.style.getImportGroups() : null;
int lastGroup = -1;
int i = 0;
// if an item will be _inserted_ at the start (= first insert after possibly some deletes, but no modifies),
// the text in between localPointer and insertPos should be copied. Insert pos may differ from estimator.getPositions()[0]
// the text should be only included for INSERT operation, so save the range. See also delete op for compensation
int insertPos = Math.min(getCommentCorrectedOldPos(oldList.get(i)), estimator.getInsertPos(0));
int insertSaveLocalPointer = localPointer;
if (insertPos < localPointer) {
insertPos = -1;
}
// go on, match it!
for (int j = 0; j < result.length; j++) {
ResultItem<JCTree> item = result[j];
int group = -1;
if (importGroups != null) {
Name name = printer.fullName(((JCImport)item.element).qualid);
group = (name != null ? importGroups.getGroupId(name.toString(), ((JCImport)item.element).staticImport) : -1);
}
switch (item.operation) {
case MODIFY: {
lastGroup = group;
int[] bounds = estimator.getPositions(i);
// replace from the actual start, leaving whitespace, currently defined only for MemberEstimator
bounds[0] = Math.min(bounds.length > 4 ? bounds[4] : bounds[0], getCommentCorrectedOldPos(oldList.get(i)));
copyTo(localPointer, bounds[0], printer);
localPointer = diffTree(oldList.get(i), item.element, bounds);
lastdel = null;
++i;
insertPos = -1;
break;
}
case INSERT: {
boolean insetBlankLine = lastGroup >= 0 && lastGroup != group;
JCTree ld = null;
boolean match = false;
if (lastdel != null) {
boolean wasInFieldGroup = false;
// PENDING - should it be tested also in the loop of *all* deleted items ? Originally both the
// FieldGroup and others only cared about the lastdel Tree.
if(lastdel instanceof FieldGroupTree) {
FieldGroupTree fieldGroupTree = (FieldGroupTree) lastdel;
for (JCVariableDecl var : fieldGroupTree.getVariables()) {
if(treesMatch(item.element, var, false)) {
ld = lastdel;
wasInFieldGroup = true;
oldTrees.remove(item.element);
break;
}
}
}
match = wasInFieldGroup;
for (Iterator<JCTree> it = deletedItems.iterator(); !match && it.hasNext(); ) {
ld = it.next();
if (match = treesMatch(item.element, ld, false)) {
it.remove();
}
}
}
// if inserting at the start (after possible deletes), copy the saved content detected before the result cycle start.
if (insertPos > -1 && i > 0) {
// do not copy past the element start, diffTree will print from that pos.
if (match) {
insertPos = Math.min(insertPos, i < oldList.size() ? estimator.getPositions(i)[0] : estimator.getPositions(i-1)[2]);
}
if (insertPos > insertSaveLocalPointer) {
copyTo(insertSaveLocalPointer, insertPos);
}
localPointer = Math.max(localPointer, insertPos);
}
insertPos = -1;
lastGroup = group;
int pos = importGroups != null ? i == 0 || insetBlankLine && i < oldList.size() ? estimator.getPositions(i)[0] : estimator.getPositions(i-1)[2]
: estimator.getInsertPos(i);
if (pos > localPointer) {
copyTo(localPointer, localPointer = pos);
}
if (insetBlankLine)
printer.blankline();
if(match) {
VeryPretty oldPrinter = this.printer;
int old = oldPrinter.indent();
this.printer = new VeryPretty(diffContext, diffContext.style, tree2Tag, tree2Doc, tag2Span, origText, oldPrinter.toString().length() + oldPrinter.getInitialOffset());//XXX
this.printer.reset(old, oldPrinter.out.getCol());
this.printer.oldTrees = oldTrees;
int index = oldList.indexOf(ld);
int[] poss = estimator.getPositions(index);
//TODO: should the original text between the return position of the following method and poss[1] be copied into the new text?
int diffTo = diffTree(ld, item.element, poss);
copyTo(diffTo, poss[1]);
localPointer = Math.max(localPointer, poss[1]);
if (j > 0 && LineInsertionType.BEFORE == estimator.lineInsertType()) printer.newline();
printer.print(this.printer.toString());
if (j < result.length -1 && LineInsertionType.AFTER == estimator.lineInsertType()) printer.newline();
printer.reindentRegions.addAll(this.printer.reindentRegions);
this.printer = oldPrinter;
this.printer.undent(old);
if (deletedItems.isEmpty()) {
lastdel = null;
}
break;
}
if (LineInsertionType.BEFORE == estimator.lineInsertType()) printer.newline();
// specific case: the item is shuffled within the same parent. It's not expected that the printer will print it with the usual
// codestyle-defined blanklines before/after. However the blank lines are more expected when the item shifts to another scope.
if (!oldList.contains(item.element) || !printer.handlePossibleOldTrees(Collections.singletonList(item.element), true)) {
printer.print(item.element);
}
if (LineInsertionType.AFTER == estimator.lineInsertType()) printer.newline();
break;
}
case DELETE: {
int[] pos = estimator.getPositions(i);
if (localPointer < pos[0] && lastdel == null) {
// 1st delete in a chain
copyTo(localPointer, pos[0], printer);
if (insertPos > -1) {
// no insert, no modify == first delete ever. Since some chars were copied from localPointer == saveLocalPointer,
// adjust the copy start for insert.
assert localPointer == insertSaveLocalPointer;
insertSaveLocalPointer = pos[0];
}
}
if (lastdel == null) {
deletedItems.clear();
}
lastdel = oldList.get(i);
deletedItems.add( lastdel );
CommentSet ch = comments.getComments(oldList.get(i));
localPointer = Math.max(pos[1], Math.max(commentEnd(ch, CommentSet.RelativePosition.INLINE), commentEnd(ch, CommentSet.RelativePosition.TRAILING)));
++i;
break;
}
case NOCHANGE: {
boolean insetBlankLine = lastGroup >= 0 && lastGroup != group;
insertPos = -1;
lastGroup = group;
int[] pos = estimator.getPositions(i);
// I don't know the reason for i != 0 (do not copy prefix for 1st item ??). Anyway, if insertion happens,
// the prefix should be probably copied.
if (pos[0] > localPointer) {
// print fill-in
copyTo(localPointer, pos[0], printer);
localPointer = pos[0];
}
if (insetBlankLine)
printer.blankline();
// handle possible comment change
JCTree oldItem = oldList.get(i);
CommentSet cs = comments.getComments(oldItem);
boolean reprintComments = cs.hasChanges();
int itemStart = pos[0];
int itemEnd = pos[1];
if (reprintComments) {
// comments are going to be printed anew, copy just the part
// covered by the item itself
itemStart = pos.length > 6 ? pos[5] : getOldPos(oldItem);
itemEnd = pos.length > 6 ? pos[6] : endPos(oldItem);
localPointer = diffPrecedingComments(oldItem, oldItem, itemStart, localPointer, false);
}
if (pos[0] >= localPointer) {
localPointer = itemStart;
if (pos.length > 3 && pos[3] != (-1) && j + 1 < result.length) {
copyTo(localPointer, localPointer = pos[3], printer);
printer.print(estimator.append(i));
}
}
copyTo(localPointer, localPointer = itemEnd, printer);
if (reprintComments) {
localPointer = diffInnerComments(oldItem, oldItem, localPointer);
localPointer = diffTrailingComments(oldItem, oldItem, localPointer, pos[1]);
}
lastdel = null;
++i;
break;
}
}
}
return localPointer;
}
/**
* Check the JCVariableDecl tree has var type
* @param tree instance of JCVariableDecl
* @return true if tree contains var type else return false
*/
private static boolean isVarTypeVariable(JCVariableDecl tree){
if(tree == null) return false;
return tree.getType() instanceof JCIdent && ((JCIdent)tree.getType()).name.contentEquals("var"); // NOI18N
}
/**
* Retrieves comment set for the specified tree t. The FieldGroupTree is handled specially:
* preceding commenst are taken from the FG's first item, following comments from the last item
* <p/>
* The return may be NEGATIVE to indicate, that the comment set is the same and should be retained
* in the output. If the value is POSITIVE, the method has handled the copying.
*
* @param t
* @param preceding
* @return
*/
private CommentSet getCommentsForTree(Tree t, boolean preceding) {
if (t instanceof FieldGroupTree) {
FieldGroupTree fgt = (FieldGroupTree)t;
List<JCVariableDecl> vars = fgt.getVariables();
t = preceding ? vars.get(0) : vars.get(vars.size() - 1);
}
return comments.getComments(t);
}
protected int diffInnerComments(JCTree oldT, JCTree newT, int localPointer) {
if (innerCommentsProcessed) {
return localPointer;
}
innerCommentsProcessed = true;
CommentSet cs = getCommentsForTree(newT, true);
CommentSet old = getCommentsForTree(oldT, true);
List<Comment> oldPrecedingComments = cs == old ? ((CommentSetImpl)cs).getOrigComments(CommentSet.RelativePosition.INNER) : old.getComments(CommentSet.RelativePosition.INNER);
List<Comment> newPrecedingComments = cs.getComments(CommentSet.RelativePosition.INNER);
if (sameComments(oldPrecedingComments, newPrecedingComments)) {
if (oldPrecedingComments.isEmpty()) {
return localPointer;
}
// WHITESPACE comments have pos == -1, and no real useful data
Comment c = oldPrecedingComments.get(oldPrecedingComments.size() - 1);
if (c.pos() == -1) {
return localPointer;
}
int newP = c.endPos();
copyTo(localPointer, newP);
return newP;
}
return diffCommentLists(getOldPos(oldT), oldPrecedingComments, newPrecedingComments, null, null, true, false, true,
false,
localPointer);
}
private DocCommentTree getDocComment(JCTree t, boolean old) {
if (t instanceof FieldGroupTree) {
FieldGroupTree fgt = (FieldGroupTree)t;
List<JCVariableDecl> vars = fgt.getVariables();
t = vars.get(0);
}
return old ? oldTopLevel.docComments.getCommentTree(t) : tree2Doc.get(t);
}
// note: the oldTreeStartPos must be the real start, without preceding comments.
protected int diffPrecedingComments(JCTree oldT, JCTree newT, int oldTreeStartPos, int localPointer, boolean doNotDelete) {
if (parent instanceof FieldGroupTree) {
FieldGroupTree fgt = (FieldGroupTree)parent;
if (!fgt.getVariables().isEmpty() && fgt.getVariables().get(0) == oldT) {
return localPointer;
}
}
CommentSet cs = getCommentsForTree(newT, true);
CommentSet old = getCommentsForTree(oldT, true);
List<Comment> oldPrecedingComments = cs == old ? ((CommentSetImpl)cs).getOrigComments(CommentSet.RelativePosition.PRECEDING) : old.getComments(CommentSet.RelativePosition.PRECEDING);
List<Comment> newPrecedingComments = cs.getComments(CommentSet.RelativePosition.PRECEDING);
DocCommentTree newD = getDocComment(newT, false);
if (sameComments(oldPrecedingComments, newPrecedingComments) && newD == null) {
if (oldPrecedingComments.isEmpty()) {
return localPointer;
}
int newP = oldPrecedingComments.get(oldPrecedingComments.size() - 1).endPos();
if (newP > localPointer && newP < oldTreeStartPos) {
copyTo(localPointer, newP);
return newP;
} else {
return localPointer;
}
}
DocCommentTree oldD = getDocComment(oldT, true);
return diffCommentLists(oldTreeStartPos, oldPrecedingComments, newPrecedingComments, oldD, newD, false, true, false,
doNotDelete,
localPointer);
}
protected int diffTrailingComments(JCTree oldT, JCTree newT, int localPointer, int elementEndWithComments) {
CommentSet cs = getCommentsForTree(newT, false);
CommentSet old = getCommentsForTree(oldT, false);
List<Comment> oldInlineComments = cs == old ? ((CommentSetImpl)cs).getOrigComments(CommentSet.RelativePosition.INLINE) : old.getComments(CommentSet.RelativePosition.INLINE);
List<Comment> newInlineComments = cs.getComments(CommentSet.RelativePosition.INLINE);
List<Comment> oldTrailingComments = cs == old ? ((CommentSetImpl)cs).getOrigComments(CommentSet.RelativePosition.TRAILING) : old.getComments(CommentSet.RelativePosition.TRAILING);
List<Comment> newTrailingComments = cs.getComments(CommentSet.RelativePosition.TRAILING);
boolean sameInline = sameComments(oldInlineComments, newInlineComments);
if (sameInline && sameComments(oldTrailingComments, newTrailingComments)) {
// copy the comments
if (oldInlineComments.isEmpty() && oldTrailingComments.isEmpty()) {
return localPointer;
}
copyTo(localPointer, localPointer = elementEndWithComments);
return localPointer;
}
//XXX: hack: the upper diff might already add '\n' to the result, need to skip it if diffing inline comments
if (!sameInline) {
while (printer.out.isWhitespaceLine()) {
printer.eatChars(1);
}
localPointer = diffCommentLists(getOldPos(oldT), oldInlineComments, newInlineComments, null, null, false, false, false, false, localPointer);
boolean containedEmbeddedNewLine = false;
boolean containsEmbeddedNewLine = false;
for (Comment oldComment : oldInlineComments) {
if (oldComment.style() == Style.LINE) containedEmbeddedNewLine = true;
}
for (Comment nueComment : newInlineComments) {
if (nueComment.style() == Style.LINE) containsEmbeddedNewLine = true;
}
if (containedEmbeddedNewLine && !containsEmbeddedNewLine) {
printer.print("\n");
}
}
int lp = diffCommentLists(getOldPos(oldT), oldTrailingComments, newTrailingComments, null, null, true, false, false, false, localPointer);
boolean commentsCreated = oldInlineComments.isEmpty() && oldTrailingComments.isEmpty();
// if comments were added, it may be possible that the immediately following newline
// was printed as a part of traling new comment. In that, suppress the immediate
// newline:
if (commentsCreated) {
Comment c = newTrailingComments.isEmpty() ?
newInlineComments.get(newInlineComments.size() - 1) :
newTrailingComments.get(newTrailingComments.size() - 1);
if (c.style() != Comment.Style.LINE) {
while (printer.out.isWhitespaceLine()) {
printer.eatChars(1);
}
}
// we have created a comment; if the local pointer is at the beginning of a new line,
// we should reset the printer to the line start as well
if (lp > 0 && diffContext.origText.charAt(lp - 1) == '\n') {
printer.out.toLineStart();
}
}
return lp;
}
private boolean sameComments(List<Comment> oldList, List<Comment> newList) {
Iterator<Comment> oldIter = oldList.iterator();
Iterator<Comment> newIter = newList.iterator();
Comment oldC = safeNext(oldIter);
Comment newC = safeNext(newIter);
while (oldC != null && newC != null) {
if (!commentsMatch(oldC, newC)) return false;
oldC = safeNext(oldIter);
newC = safeNext(newIter);
}
return !((oldC == null) ^ (newC == null));
}
// refactor it! make it better
private int diffCommentLists(int oldTreeStartPos, List<Comment> oldList,
List<Comment> newList, DocCommentTree oldDoc, DocCommentTree newDoc, boolean trailing, boolean preceding, boolean inner,
boolean doNotDeleteIfMissing,
int localPointer) {
Comment javadoc = null;
for (Comment comment : oldList) {
if(comment.style() == Style.JAVADOC) {
javadoc = comment;
}
}
Iterator<Comment> oldIter = oldList.iterator();
Iterator<Comment> newIter = newList.iterator();
Comment oldC = safeNext(oldIter);
Comment newC = safeNext(newIter);
boolean first = true;
boolean firstNewCommentPrinted = false;
while (oldC != null && newC != null) {
int cStart = commentStartCorrect(oldC);
if (first && trailing && localPointer < cStart) {
copyTo(localPointer, cStart);
}
first = false;
int nextTarget = Math.max(localPointer, oldC.endPos());
if (commentsMatch(oldC, newC)) {
if(preceding && oldC == javadoc && oldDoc != null) {
localPointer = diffDocTree((DCDocComment)oldDoc, (DCTree)oldDoc, (DCTree)newDoc, new int[]{localPointer, oldC.endPos()});
}
if (nextTarget > localPointer) {
copyTo(localPointer, nextTarget);
}
oldC = safeNext(oldIter);
newC = safeNext(newIter);
firstNewCommentPrinted = true;
} else if (!listContains(newList, oldC)) {
if (!listContains(oldList, newC)) {
// append(Diff.modify(oldT, newT, oldC, newC));
copyTo(localPointer, localPointer = oldC.pos());
printer.printComment(newC, !trailing, false, true);
oldC = safeNext(oldIter);
newC = safeNext(newIter);
} else {
// append(Diff.delete(oldT, newT, oldC));
oldC = safeNext(oldIter);
}
} else {
if (!firstNewCommentPrinted && preceding) {
copyTo(localPointer, localPointer = oldTreeStartPos);
}
printer.print(newC.getText());
newC = safeNext(newIter);
firstNewCommentPrinted = true;
}
localPointer = nextTarget;
}
while (oldC != null) {
// append(Diff.delete(oldT, newT, oldC));
int cStart = commentStartCorrect(oldC);
if (first && trailing && localPointer < cStart) {
copyTo(localPointer, cStart);
}
// special handling for whitespace to be preserved:
if (oldC.style() == Style.WHITESPACE) {
localPointer = cStart;
} else if (first && doNotDeleteIfMissing) {
localPointer = cStart;
} else {
first = false;
localPointer = Math.max(localPointer, oldC.endPos());
}
oldC = safeNext(oldIter);
}
while (newC != null) {
if (Style.WHITESPACE != newC.style()) {
// printer.print(newC.getText());
if (!firstNewCommentPrinted && preceding) {
copyTo(localPointer, localPointer = oldTreeStartPos);
}
printer.printComment(newC, !trailing, false, !preceding && !trailing);
firstNewCommentPrinted = true;
}
newC = safeNext(newIter);
}
if(preceding && javadoc == null && newDoc != null) {
if (!firstNewCommentPrinted && preceding) {
copyTo(localPointer, localPointer = oldTreeStartPos);
}
// suppress potential margin after doc comment: there's a whitespace ready between the comment and the
// JCTree.
printer.print((DCTree) newDoc, firstNewCommentPrinted);
}
return localPointer;
}
private int diffDocTree(DCDocComment doc, DCTree oldT, DCTree newT, int[] elementBounds) {
if (oldT == null && newT != null) {
throw new IllegalArgumentException("Null is not allowed in parameters.");
}
if (oldT == newT) {
return elementBounds[0];
}
if (newT == null) {
tokenSequence.move(elementBounds[1]);
if (!tokenSequence.moveNext()) {
return elementBounds[1];
}
while (tokenSequence.token().id() == JavaTokenId.WHITESPACE && tokenSequence.moveNext()) {
// Skip whitespace
}
return tokenSequence.offset();
}
int localpointer = elementBounds[0];
if (oldT.getKind() != newT.getKind()) {
// different kind of trees found, print the whole new one.
int[] oldBounds = getBounds(oldT, doc);
if (oldBounds[0] > elementBounds[0]) {
copyTo(elementBounds[0], oldBounds[0]);
}
printer.print(newT);
return oldBounds[1];
}
switch(oldT.getKind()) {
case ATTRIBUTE:
localpointer = diffAttribute(doc, (DCAttribute) oldT, (DCAttribute) newT, elementBounds);
break;
case DOC_COMMENT:
localpointer = diffDocComment(doc, (DCDocComment) oldT, (DCDocComment) newT, elementBounds);
break;
case PARAM:
localpointer = diffParam(doc, (DCParam) oldT, (DCParam) newT, elementBounds);
break;
case RETURN:
localpointer = diffReturn(doc, (DCReturn) oldT, (DCReturn) newT, elementBounds);
break;
case IDENTIFIER:
localpointer = diffIdentifier(doc, (DCIdentifier) oldT, (DCIdentifier) newT, elementBounds);
break;
case SEE:
localpointer = diffSee(doc, (DCSee) oldT, (DCSee) newT, elementBounds);
break;
/**
* Used for instances of {@link LinkTree} representing an @linkplain tag.
*/
case LINK_PLAIN:
case LINK:
localpointer = diffLink(doc, (DCLink)oldT, (DCLink)newT, elementBounds);
break;
case TEXT:
localpointer = diffText(doc, (DCText)oldT, (DCText)newT, elementBounds);
break;
case AUTHOR:
localpointer = diffAuthor(doc, (DCAuthor)oldT, (DCAuthor)newT, elementBounds);
break;
case COMMENT:
localpointer = diffComment(doc, (DCComment)oldT, (DCComment)newT, elementBounds);
break;
case DEPRECATED:
localpointer = diffDeprecated(doc, (DCDeprecated)oldT, (DCDeprecated)newT, elementBounds);
break;
case DOC_ROOT:
localpointer = diffDocRoot(doc, (DCDocRoot)oldT, (DCDocRoot)newT, elementBounds);
break;
case ENTITY:
localpointer = diffEntity(doc, (DCEntity)oldT, (DCEntity)newT, elementBounds);
break;
case ERRONEOUS:
localpointer = diffErroneous(doc, (DCErroneous)oldT, (DCErroneous)newT, elementBounds);
break;
/**
* Used for instances of {@link ThrowsTree} representing an
*
* @exception tag.
*/
case EXCEPTION:
case THROWS:
localpointer = diffThrows(doc, (DCThrows)oldT, (DCThrows)newT, elementBounds);
break;
case INHERIT_DOC:
localpointer = diffInheritDoc(doc, (DCInheritDoc)oldT, (DCInheritDoc)newT, elementBounds);
break;
/**
* Used for instances of {@link LiteralTree} representing an @code tag.
*/
case CODE:
case LITERAL:
localpointer = diffLiteral(doc, (DCLiteral)oldT, (DCLiteral)newT, elementBounds);
break;
case REFERENCE:
localpointer = diffReference(doc, (DCReference)oldT, (DCReference)newT, elementBounds);
break;
case SERIAL:
localpointer = diffSerial(doc, (DCSerial)oldT, (DCSerial)newT, elementBounds);
break;
case SERIAL_DATA:
localpointer = diffSerialData(doc, (DCSerialData)oldT, (DCSerialData)newT, elementBounds);
break;
case SERIAL_FIELD:
localpointer = diffSerialField(doc, (DCSerialField)oldT, (DCSerialField)newT, elementBounds);
break;
case SINCE:
localpointer = diffSince(doc, (DCSince)oldT, (DCSince)newT, elementBounds);
break;
/**
* Used for instances of {@link EndElementTree} representing the
* start of an HTML element.
*/
case START_ELEMENT:
localpointer = diffStartElement(doc, (DCStartElement)oldT, (DCStartElement)newT, elementBounds);
break;
case END_ELEMENT:
localpointer = diffEndElement(doc, (DCEndElement)oldT, (DCEndElement)newT, elementBounds);
break;
case UNKNOWN_BLOCK_TAG:
localpointer = diffUnknownBlockTag(doc, (DCUnknownBlockTag)oldT, (DCUnknownBlockTag)newT, elementBounds);
break;
case UNKNOWN_INLINE_TAG:
localpointer = diffUnknownInlineTag(doc, (DCUnknownInlineTag)oldT, (DCUnknownInlineTag)newT, elementBounds);
break;
case VALUE:
localpointer = diffValue(doc, (DCValue)oldT, (DCValue)newT, elementBounds);
break;
case VERSION:
localpointer = diffVersion(doc, (DCVersion)oldT, (DCVersion)newT, elementBounds);
break;
default:
// handle special cases like field groups and enum constants
if (oldT.getKind() == DocTree.Kind.OTHER) {
// if (oldT instanceof FieldGroupTree) {
// return diffFieldGroup((FieldGroupTree) oldT, (FieldGroupTree) newT, elementBounds);
// }
break;
}
String msg = "Diff not implemented: "
+ ((com.sun.source.doctree.DocTree) oldT).getKind().toString()
+ " " + oldT.getClass().getName();
throw new AssertionError(msg);
}
return localpointer;
}
private int diffAttribute(DCDocComment doc, DCAttribute oldT, DCAttribute newT, int[] elementBounds) {
return elementBounds[1];
}
private int diffDocComment(DCDocComment doc, DCDocComment oldT, DCDocComment newT, int[] elementBounds) {
tokenSequence.move(elementBounds[0]);
if (!tokenSequence.moveNext()) {
return elementBounds[1];
}
while (tokenSequence.token().id() == JavaTokenId.WHITESPACE && tokenSequence.moveNext()) {
// Skip whitespace
}
int localpointer = tokenSequence.offset() + 3; // copy the first characters of the javadoc comment /**;
// int localpointer = getOldPos((DCTree)oldT.getFirstSentence().head, doc);
copyTo(elementBounds[0], localpointer);
if(oldT.firstSentence.isEmpty() && !newT.firstSentence.isEmpty()) {
printer.newline();
printer.toLeftMargin();
printer.print(" * ");
}
localpointer = diffList(doc, oldT.firstSentence, newT.firstSentence, localpointer, Measure.TAGS);
localpointer = diffList(doc, oldT.body, newT.body, localpointer, Measure.TAGS);
if(oldT.tags.isEmpty()) {
int commentEnd = commentEnd(doc);
if(localpointer < commentEnd) {
copyTo(localpointer, localpointer = commentEnd);
}
}
localpointer = diffList(doc, oldT.tags, newT.tags, localpointer, Measure.TAGS);
// localpointer = endPos(oldT.tags, doc);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffParam(DCDocComment doc, DCParam oldT, DCParam newT, int[] elementBounds) {
int localpointer;
if(oldT.isTypeParameter != newT.isTypeParameter) {
if(oldT.isTypeParameter) {
localpointer = getOldPos(oldT.name, doc);
copyTo(elementBounds[0], localpointer - 1);
} else {
localpointer = getOldPos(oldT.name, doc);
copyTo(elementBounds[0], localpointer);
printer.print("<");
}
} else {
localpointer = getOldPos(oldT.name, doc);
copyTo(elementBounds[0], localpointer);
}
int nameEnd = endPos(oldT.name, doc);
localpointer = diffDocTree(doc, oldT.name, newT.name, new int[] {localpointer, nameEnd});
if(localpointer < nameEnd) {
copyTo(localpointer, localpointer = nameEnd);
}
if(oldT.isTypeParameter) {
localpointer++;
}
if(newT.isTypeParameter) {
printer.print(">");
}
localpointer = diffList(doc, oldT.description, newT.description, localpointer, Measure.TAGS);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffReturn(DCDocComment doc, DCReturn oldT, DCReturn newT, int[] elementBounds) {
int localpointer = oldT.description.isEmpty()? elementBounds[1] : getOldPos(oldT.description.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.description, newT.description, localpointer, Measure.TAGS);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffIdentifier(DCDocComment doc, DCIdentifier oldT, DCIdentifier newT, int[] elementBounds) {
if(oldT.name.equals(newT.name)) {
copyTo(elementBounds[0], elementBounds[1]);
} else {
printer.print((Name) newT.name);
}
return elementBounds[1];
}
private int diffLink(DCDocComment doc, DCLink oldT, DCLink newT, int[] elementBounds) {
int localpointer = getOldPos(oldT.ref, doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffDocTree(doc, oldT.ref, newT.ref, new int[] {localpointer, endPos(oldT.ref, doc)});
localpointer = diffList(doc, oldT.label, newT.label, localpointer, Measure.TAGS);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffSee(DCDocComment doc, DCSee oldT, DCSee newT, int[] elementBounds) {
int localpointer;
localpointer = getOldPos(oldT.reference.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.reference, newT.reference, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffText(DCDocComment doc, DCText oldT, DCText newT, int[] elementBounds) {
if(oldT.text.equals(newT.text)) {
copyTo(elementBounds[0], elementBounds[1]);
} else {
printer.print(newT.text);
}
return elementBounds[1];
}
private int diffAuthor(DCDocComment doc, DCAuthor oldT, DCAuthor newT, int[] elementBounds) {
int localpointer = oldT.name.isEmpty()? elementBounds[1] : getOldPos(oldT.name.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.name, newT.name, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffComment(DCDocComment doc, DCComment oldT, DCComment newT, int[] elementBounds) {
if(oldT.body.equals(newT.body)) {
copyTo(elementBounds[0], elementBounds[1]);
} else {
printer.print(newT.body);
}
return elementBounds[1];
}
private int diffDeprecated(DCDocComment doc, DCDeprecated oldT, DCDeprecated newT, int[] elementBounds) {
int localpointer = oldT.body.isEmpty()? elementBounds[1] : getOldPos(oldT.body.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.body, newT.body, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffDocRoot(DCDocComment doc, DCDocRoot oldT, DCDocRoot newT, int[] elementBounds) {
copyTo(elementBounds[0], elementBounds[1]);
return elementBounds[1];
}
private int diffEntity(DCDocComment doc, DCEntity oldT, DCEntity newT, int[] elementBounds) {
if(oldT.name.equals(newT.name)) {
copyTo(elementBounds[0], elementBounds[1]);
} else {
printer.print(newT);
}
return elementBounds[1];
}
private int diffErroneous(DCDocComment doc, DCErroneous oldT, DCErroneous newT, int[] elementBounds) {
if(oldT.body.equals(newT.body)) {
copyTo(elementBounds[0], elementBounds[1]);
} else {
printer.print(newT.body);
}
return elementBounds[1];
}
private int diffThrows(DCDocComment doc, DCThrows oldT, DCThrows newT, int[] elementBounds) {
int localpointer;
localpointer = getOldPos(oldT.name, doc);
copyTo(elementBounds[0], localpointer);
int endPos = endPos(oldT.name, doc);
localpointer = diffDocTree(doc, oldT.name, newT.name, new int[] {localpointer, endPos});
if(localpointer < endPos) {
copyTo(localpointer, localpointer = endPos);
}
localpointer = diffList(doc, oldT.description, newT.description, localpointer, Measure.TAGS);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffInheritDoc(DCDocComment doc, DCInheritDoc oldT, DCInheritDoc newT, int[] elementBounds) {
copyTo(elementBounds[0], elementBounds[1]);
return elementBounds[1];
}
private int diffLiteral(DCDocComment doc, DCLiteral oldT, DCLiteral newT, int[] elementBounds) {
int localpointer;
localpointer = getOldPos(oldT.body, doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffDocTree(doc, oldT.body, newT.body, new int[] {localpointer, endPos(oldT.body, doc)});
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffReference(DCDocComment doc, DCReference oldT, DCReference newT, int[] elementBounds) {
printer.print(newT);
return elementBounds[1];
}
private int diffSerial(DCDocComment doc, DCSerial oldT, DCSerial newT, int[] elementBounds) {
int localpointer = getOldPos(oldT.description.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.description, newT.description, localpointer, Measure.TAGS);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffSerialData(DCDocComment doc, DCSerialData oldT, DCSerialData newT, int[] elementBounds) {
int localpointer = getOldPos(oldT.description.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.description, newT.description, localpointer, Measure.TAGS);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffSerialField(DCDocComment doc, DCSerialField oldT, DCSerialField newT, int[] elementBounds) {
int localpointer;
localpointer = getOldPos(oldT.name, doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffDocTree(doc, oldT.name, newT.name, new int[] {localpointer, endPos(oldT.name, doc)});
localpointer = diffDocTree(doc, oldT.type, newT.type, new int[] {localpointer, endPos(oldT.type, doc)});
localpointer = diffList(doc, oldT.description, newT.description, localpointer, Measure.TAGS);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffSince(DCDocComment doc, DCSince oldT, DCSince newT, int[] elementBounds) {
int localpointer = oldT.body.isEmpty()? elementBounds[1] : getOldPos(oldT.body.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.body, newT.body, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffStartElement(DCDocComment doc, DCStartElement oldT, DCStartElement newT, int[] elementBounds) {
int localpointer = oldT.attrs.isEmpty()? elementBounds[1] - 1 : getOldPos(oldT.attrs.get(0), doc);
if(oldT.name.equals(newT.name)) {
copyTo(elementBounds[0], localpointer);
} else {
printer.print("<");
printer.print((Name) newT.name);
}
localpointer = diffList(doc, oldT.attrs, newT.attrs, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffEndElement(DCDocComment doc, DCEndElement oldT, DCEndElement newT, int[] elementBounds) {
if(oldT.name.equals(newT.name)) {
copyTo(elementBounds[0], elementBounds[1]);
} else {
printer.print(newT);
}
return elementBounds[1];
}
private int diffUnknownBlockTag(DCDocComment doc, DCUnknownBlockTag oldT, DCUnknownBlockTag newT, int[] elementBounds) {
int localpointer = oldT.content.isEmpty()? elementBounds[1] : getOldPos(oldT.content.get(0), doc);
if(oldT.name.equals(newT.name)) {
copyTo(elementBounds[0], localpointer);
} else {
printer.print("@"); //NOI18N
printer.print((Name) newT.name);
printer.out.needSpace();
}
localpointer = diffList(doc, oldT.content, newT.content, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffUnknownInlineTag(DCDocComment doc, DCUnknownInlineTag oldT, DCUnknownInlineTag newT, int[] elementBounds) {
int localpointer = oldT.content.isEmpty()? elementBounds[1] : getOldPos(oldT.content.get(0), doc);
if(oldT.name.equals(newT.name)) {
copyTo(elementBounds[0], localpointer);
} else {
printer.print("{@"); //NOI18N
printer.print((Name) newT.name);
printer.out.needSpace();
}
localpointer = diffList(doc, oldT.content, newT.content, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffValue(DCDocComment doc, DCValue oldT, DCValue newT, int[] elementBounds) {
int localpointer;
localpointer = getOldPos(oldT.ref, doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffDocTree(doc, oldT.ref, newT.ref, new int[] {localpointer, endPos(oldT.ref, doc)});
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffVersion(DCDocComment doc, DCVersion oldT, DCVersion newT, int[] elementBounds) {
int localpointer;
localpointer = oldT.body.isEmpty() ? elementBounds[1] : getOldPos(oldT.body.get(0), doc);
copyTo(elementBounds[0], localpointer);
localpointer = diffList(doc, oldT.body, newT.body, localpointer, Measure.DOCTREE);
if(localpointer < elementBounds[1]) {
copyTo(localpointer, elementBounds[1]);
}
return elementBounds[1];
}
private int diffList(
DCDocComment doc,
List<? extends DCTree> oldList,
List<? extends DCTree> newList,
int localPointer,
Comparator<DCTree> measure)
{
assert oldList != null && newList != null;
if (oldList == newList || oldList.equals(newList)) {
return localPointer;
}
ListMatcher<DCTree> matcher = ListMatcher.<DCTree>instance(
oldList,
newList,
measure
);
if (!matcher.match()) {
return localPointer;
}
DCTree lastdel = null; // last deleted element
ResultItem<DCTree>[] result = matcher.getResult();
if (oldList.isEmpty() && !newList.isEmpty()) {
// such a situation needs special handling. It is difficult to
// obtain a correct position.
StringBuilder aHead = new StringBuilder(), aTail = new StringBuilder();
// int pos = estimator.prepare(localPointer, aHead, aTail);
// copyTo(localPointer, pos, printer);
//
// printer.print(aHead.toString());
printer.out.needSpace();
for (DCTree item : newList) {
// if (LineInsertionType.BEFORE == estimator.lineInsertType()) printer.newline();
printer.print(item);
// if (LineInsertionType.AFTER == estimator.lineInsertType()) printer.newline();
}
// printer.print(aTail.toString());
return localPointer;
}
// if there has been imports which is removed now
if (newList.isEmpty() && !oldList.isEmpty()) {
int oldPos = adjustToPreviousNewLine(getOldPos(oldList.get(0), doc), localPointer);
copyTo(localPointer, oldPos);
return endPos(oldList, doc);
}
// copy to start position
int insertPos = adjustToPreviousNewLine(getOldPos(oldList.get(0), doc), localPointer);
if (insertPos > localPointer) {
copyTo(localPointer, localPointer = insertPos);
}
// go on, match it!
int i = 0;
for (int j = 0; j < result.length; j++) {
ResultItem<DCTree> item = result[j];
switch (item.operation) {
case MODIFY: {
DCTree oldT = oldList.get(i);
int[] pos = getBounds(oldT, doc);
copyTo(localPointer, pos[0]);
localPointer = diffDocTree(doc, oldT, item.element, pos);
++i;
break;
}
case INSERT: {
int oldPos = item.element.pos;
boolean found = false;
if (oldPos > 0) {
for (DCTree oldT : oldList) {
int oldNodePos = oldT.pos;
if (oldPos == oldNodePos) {
found = true;
VeryPretty oldPrinter = this.printer;
int old = oldPrinter.indent();
this.printer = new VeryPretty(diffContext, diffContext.style, tree2Tag, tree2Doc, tag2Span, origText, oldPrinter.toString().length() + oldPrinter.getInitialOffset());//XXX
this.printer.reset(old, oldPrinter.out.getCol());
this.printer.oldTrees = oldTrees;
int[] poss = getBounds(oldT, doc);
int end = diffDocTree(doc, oldT, item.element, poss);
copyTo(end, poss[1]);
printer.print(this.printer.toString()); //XXX: this appears to copy this.printer's content into the same printer?
printer.reindentRegions.addAll(this.printer.reindentRegions);
this.printer = oldPrinter;
this.printer.undent(old);
break;
}
}
}
if (!found) {
// if (lastdel != null) {
// if(treesMatch(item.element, lastdel, false)) {
// VeryPretty oldPrinter = this.printer;
// int old = oldPrinter.indent();
// this.printer = new VeryPretty(diffContext, diffContext.style, tree2Tag, tag2Span, origText, oldPrinter.toString().length() + oldPrinter.getInitialOffset());//XXX
// this.printer.reset(old);
// this.printer.oldTrees = oldTrees;
// int index = oldList.indexOf(lastdel);
// int[] poss = estimator.getPositions(index);
// //TODO: should the original text between the return position of the following method and poss[1] be copied into the new text?
// localPointer = diffTree(lastdel, item.element, poss);
// printer.print(this.printer.toString());
// this.printer = oldPrinter;
// this.printer.undent(old);
// lastdel = null;
// break;
// }
// }
printer.print(item.element);
}
break;
}
case DELETE: {
DCTree oldT = oldList.get(i);
lastdel = oldT;
int[] pos = getBounds(oldT, doc);
// if (localPointer < pos[0] && lastdel == null) {
// copyTo(localPointer, pos[0], printer);
// }
localPointer = pos[1];
++i;
break;
}
case NOCHANGE: {
DCTree oldT = oldList.get(i);
int[] pos = getBounds(oldT, doc);
// if (pos[0] > localPointer && i != 0) {
// // print fill-in
// copyTo(localPointer, pos[0], printer);
// }
// if (pos[0] >= localPointer) {
// localPointer = pos[0];
// }
if(needStar(pos[0])) {
printer.print(" * ");
}
copyTo(localPointer, localPointer = pos[1], printer);
lastdel = null;
++i;
break;
}
}
}
return localPointer;
}
private Comment safeNext(Iterator<Comment> iter) {
return iter.hasNext() ? iter.next() : null;
}
private boolean commentsMatch(Comment oldC, Comment newC) {
if (oldC == null && newC == null)
return true;
if (oldC == null || newC == null)
return false;
return oldC.equals(newC);
}
private boolean listContains(List<Comment>list, Comment comment) {
for (Comment c : list)
if (c.equals(comment))
return true;
return false;
}
private int commentStartCorrect(Comment c) {
tokenSequence.move(c.pos());
boolean wasPrevious = false;
while (tokenSequence.movePrevious()) {
if (tokenSequence.token().id() != JavaTokenId.WHITESPACE) {
return tokenSequence.offset() + tokenSequence.token().length();
}
int lastNewLine = tokenSequence.token().text().toString().lastIndexOf('\n');
if (lastNewLine != (-1)) {
return tokenSequence.offset() + lastNewLine + 1;
}
wasPrevious = true;
}
if (wasPrevious)
return tokenSequence.offset();
else
return c.pos();
}
public static int commentStart(DiffContext diffContext, CommentSet comments, CommentSet.RelativePosition pos, int limit) {
List<Comment> list = comments.getComments(pos);
if (list.isEmpty()) {
return Integer.MAX_VALUE;
} else {
diffContext.tokenSequence.move(limit);
moveToSrcRelevant(diffContext.tokenSequence, Direction.BACKWARD);
limit = diffContext.tokenSequence.offset() + diffContext.tokenSequence.token().length();
int start = Integer.MAX_VALUE;
for (Comment c : list) {
if (c.pos() >= limit) start = Math.min(start, c.pos());
}
return start;
}
}
public static int commentEnd(CommentSet comments, CommentSet.RelativePosition pos) {
List<Comment> list = comments.getComments(pos);
if (list.isEmpty()) {
return -1;
} else {
return list.get(list.size() - 1).endPos();
}
}
private static int commentEnd(DCDocComment doc) {
int length = doc.comment.getText().length();
return doc.comment.getSourcePos(length-1);
}
private static int getOldPos(JCTree oldT) {
return TreeInfo.getStartPos(oldT);
}
private int getOldPos(DCTree oldT, DCDocComment doc) {
return (int) oldT.getSourcePosition(doc);
}
public int endPos(DCTree oldT, DCDocComment doc) {
DocSourcePositions sp = JavacTrees.instance(context).getSourcePositions();
return (int) sp.getEndPosition(null, doc, oldT);
}
private int endPos(List<? extends DCTree> trees, DCDocComment doc) {
if (trees.isEmpty())
return -1;
return endPos(trees.get(trees.size()-1), doc);
}
/**
* Create differences between trees. Old tree has to exist, i.e.
* <code>oldT != null</code>. There is a one exception - when both
* <code>oldT</code> and <code>newT</code> are null, then method
* just returns.
*
* @param oldT original tree in source code
* @param newT tree to replace the original tree
* @return position in original source
*/
protected int diffTree(JCTree oldT, JCTree newT, int[] elementBounds) {
return checkLocalPointer(oldT, newT, diffTree(oldT, newT, null, elementBounds));
}
/**
* Tracks current OLD node's path, so printout can be modified according to context
*/
private @NullAllowed TreePath currentPath;
int diffTree(TreePath oldPath, JCTree newT, int[] elementBounds) {
JCTree oldT = (JCTree)oldPath.getLeaf();
this.currentPath = oldPath;
JCTree parent = (JCTree)(oldPath.getParentPath() == null ? null : oldPath.getParentPath().getLeaf());
return diffTree(oldT, newT, parent, elementBounds);
}
protected int diffTree(JCTree oldT, JCTree newT, JCTree parent /*used only for modifiers*/, int[] elementBounds) {
TreePath savePath = currentPath;
Object t = tree2Tag.get(newT);
// on 1st entry, currentPath is already set to point to oldT
if (currentPath != null && currentPath.getLeaf() != oldT) {
currentPath = new TreePath(currentPath, oldT);
}
int result;
if (t != null) {
int start = printer.toString().length();
result = diffTreeImpl(oldT, newT, parent, elementBounds);
int end = printer.toString().length();
tag2Span.put(t, new int[]{start + printer.getInitialOffset(), end + printer.getInitialOffset()});
} else {
result = diffTreeImpl(oldT, newT, parent, elementBounds);
}
currentPath = savePath;
return result;
}
private int getPosAfterCommentEnd(Tree t, int minPos) {
CommentSet cs = getCommentsForTree(t, false);
List<Comment> cmm = cs.getComments(CommentSet.RelativePosition.TRAILING);
if (cmm.isEmpty()) {
cmm = cs.getComments(CommentSet.RelativePosition.INLINE);
}
if (cmm.isEmpty()) {
return minPos;
}
Comment c = cmm.get(cmm.size() - 1);
int pos = c.endPos();
assert pos >= 0;
if (c.style() == Comment.Style.LINE || c.style() == Comment.Style.WHITESPACE) {
// compensate trailing newline to preserve line ends for line comment and whitespace
if (pos > 0 && diffContext.origText.charAt(pos - 1) == '\n') {
pos--;
}
}
return Math.max(minPos, pos);
}
private int getPosAfterCommentStart(Tree t, int minPos) {
CommentSet cs = getCommentsForTree(t, true);
List<Comment> cmm = cs.getComments(CommentSet.RelativePosition.PRECEDING);
if (cmm.isEmpty()) {
cmm = cs.getComments(CommentSet.RelativePosition.INNER);
}
if (cmm.isEmpty()) {
return minPos;
}
Comment c = cmm.get(cmm.size() - 1);
int pos = c.endPos();
assert pos >= 0;
return Math.max(minPos, pos);
}
private int getPosAfterTreeComments(JCTree t, int end) {
class Scn extends ErrorAwareTreeScanner<Void, Void> {
int max = -1;
@Override
public Void scan(Tree node, Void p) {
max = Math.max(getPosAfterCommentEnd((JCTree)node, -1), max);
return super.scan(node, p);
}
}
Scn scn = new Scn();
scn.scan(t, null);
return Math.max(scn.max, end);
}
/**
* True, if inner comments have been processed by a specialized code, or they will be printed after the tree.
* Inner comments are used rarely, but e.g. blocks and classes use inner comments if they have empty body.
* The flag is valid only throughout invocation of {@link #diffTreeImpl}. It is reset at the start in a hope
* that custom code will properly place inner comments and sets the flag. If it is still unset at the end of the method,
* diffTreeImpl will print the comments. At the end, the flag is reset to the original value
*/
private boolean innerCommentsProcessed;
private JCTree parent;
protected int diffTreeImpl(JCTree oldT, JCTree newT, JCTree parent /*used only for modifiers*/, int[] elementBounds) {
boolean saveInnerComments = this.innerCommentsProcessed;
JCTree saveParent = this.parent;
this.parent = parent;
int ret = diffTreeImpl0(oldT, newT, parent, elementBounds);
this.innerCommentsProcessed = saveInnerComments;
this.parent = saveParent;
return ret;
}
private int diffTreeImpl0(JCTree oldT, JCTree newT, JCTree parent /*used only for modifiers*/, int[] elementBounds) {
innerCommentsProcessed = false;
if (oldT == null && newT != null)
throw new IllegalArgumentException("Null is not allowed in parameters.");
if (oldT == newT)
return elementBounds[0];
if (newT == null) {
tokenSequence.move(elementBounds[1]);
if (!tokenSequence.moveNext()) {
return elementBounds[1];
}
while (tokenSequence.token().id() == JavaTokenId.WHITESPACE && tokenSequence.moveNext())
;
return tokenSequence.offset();
}
if (printer.handlePossibleOldTrees(Collections.singletonList(newT), true)) {
return getCommentCorrectedEndPos(oldT);
}
boolean handleImplicitLambda = parent != null && parent.hasTag(Tag.LAMBDA) && ((JCLambda)parent).params.size() == 1
&& ((JCLambda)parent).params.get(0) == oldT &&((JCLambda)parent).paramKind == JCLambda.ParameterKind.IMPLICIT
&& newT.hasTag(Tag.VARDEF) && ((JCVariableDecl)newT).getType() != null;
if (handleImplicitLambda) {
tokenSequence.move(getOldPos(parent));
if (tokenSequence.moveNext() && tokenSequence.token().id() == JavaTokenId.LPAREN) {
handleImplicitLambda = false;
} else {
printer.print("(");
}
}
if (oldT.getTag() != newT.getTag()) {
if (((compAssign.contains(oldT.getKind()) && compAssign.contains(newT.getKind())) == false) &&
((binaries.contains(oldT.getKind()) && binaries.contains(newT.getKind())) == false) &&
((unaries.contains(oldT.getKind()) && unaries.contains(newT.getKind())) == false)) {
// different kind of trees found, print the whole new one.
int[] oldBounds = getBounds(oldT);
elementBounds[0] = getPosAfterCommentStart(oldT, elementBounds[0]);
if (oldBounds[0] > elementBounds[0]) {
copyTo(elementBounds[0], oldBounds[0]);
}
printer.print(newT);
// the printer will print attached traling comments, skip in the obsolete comments in the stream, so they don't duplicate.
return getPosAfterTreeComments(oldT, oldBounds[1]);
}
}
// if comments are the same, diffPredComments will skip them so that printer.print(newT) will
// not emit them from the new element. But if printer.print() won't be used (the newT will be merged in rather
// than printed anew), then surviving comments have to be printed.
int predComments = diffPrecedingComments(oldT, newT, getOldPos(oldT), elementBounds[0],
oldT.getTag() == Tag.TOPLEVEL && diffContext.forceInitialComment);
int retVal = -1;
// if (predComments < 0 && elementBounds[0] < -predComments) {
// copyTo(elementBounds[0], -predComments);
// }
elementBounds[0] = Math.abs(predComments);
int elementEnd = elementBounds[1];
int commentsStart = Math.min(commentStart(diffContext, comments.getComments(oldT), CommentSet.RelativePosition.INLINE, endPos(oldT)), commentStart(diffContext, comments.getComments(oldT), CommentSet.RelativePosition.TRAILING, endPos(oldT)));
if (commentsStart < elementBounds[1]) {
int lastIndex;
tokenSequence.move(commentsStart);
elementBounds = Arrays.copyOf(elementBounds, elementBounds.length);
elementBounds[1] = tokenSequence.movePrevious() && tokenSequence.token().id() == JavaTokenId.WHITESPACE &&
(lastIndex = tokenSequence.token().text().toString().lastIndexOf('\n')) > -1 ?
tokenSequence.offset() + lastIndex + 1 : commentsStart;
}
switch (oldT.getTag()) {
case TOPLEVEL:
diffTopLevel((JCCompilationUnit)oldT, (JCCompilationUnit)newT, elementBounds);
break;
case MODULEDEF:
retVal = diffModuleDef((JCModuleDecl)oldT, (JCModuleDecl)newT, elementBounds);
break;
case REQUIRES:
retVal = diffRequires((JCRequires)oldT, (JCRequires)newT, elementBounds);
break;
case EXPORTS:
retVal = diffExports((JCExports)oldT, (JCExports)newT, elementBounds);
break;
case OPENS:
retVal = diffOpens((JCOpens)oldT, (JCOpens)newT, elementBounds);
break;
case PROVIDES:
retVal = diffProvides((JCProvides)oldT, (JCProvides)newT, elementBounds);
break;
case USES:
retVal = diffUses((JCUses)oldT, (JCUses)newT, elementBounds);
break;
case PACKAGEDEF:
retVal = diffPackage((JCPackageDecl)oldT, (JCPackageDecl)newT, getOldPos(oldT));
break;
case IMPORT:
retVal = diffImport((JCImport)oldT, (JCImport)newT, elementBounds);
break;
case CLASSDEF:
retVal = diffClassDef((JCClassDecl)oldT, (JCClassDecl)newT, elementBounds);
break;
case METHODDEF:
retVal = diffMethodDef((JCMethodDecl)oldT, (JCMethodDecl)newT, elementBounds);
break;
case VARDEF:
retVal = diffVarDef((JCVariableDecl)oldT, (JCVariableDecl)newT, elementBounds);
break;
case SKIP:
copyTo(elementBounds[0], elementBounds[1]);
retVal = elementBounds[1];
break;
case BLOCK:
retVal = diffBlock((JCBlock)oldT, (JCBlock)newT, elementBounds);
break;
case DOLOOP:
retVal = diffDoLoop((JCDoWhileLoop)oldT, (JCDoWhileLoop)newT, elementBounds);
break;
case WHILELOOP:
retVal = diffWhileLoop((JCWhileLoop)oldT, (JCWhileLoop)newT, elementBounds);
break;
case FORLOOP:
retVal = diffForLoop((JCForLoop)oldT, (JCForLoop)newT, elementBounds);
break;
case FOREACHLOOP:
retVal = diffForeachLoop((JCEnhancedForLoop)oldT, (JCEnhancedForLoop)newT, elementBounds);
break;
case LABELLED:
retVal = diffLabelled((JCLabeledStatement)oldT, (JCLabeledStatement)newT, elementBounds);
break;
case SWITCH:
retVal = diffSwitch((JCSwitch)oldT, (JCSwitch)newT, elementBounds);
break;
case CASE:
retVal = diffCase((JCCase)oldT, (JCCase)newT, elementBounds);
break;
case SYNCHRONIZED:
retVal = diffSynchronized((JCSynchronized)oldT, (JCSynchronized)newT, elementBounds);
break;
case TRY:
retVal = diffTry((JCTry)oldT, (JCTry)newT, elementBounds);
break;
case CATCH:
retVal = diffCatch((JCCatch)oldT, (JCCatch)newT, elementBounds);
break;
case CONDEXPR:
retVal = diffConditional((JCConditional)oldT, (JCConditional)newT, elementBounds);
break;
case IF:
retVal = diffIf((JCIf)oldT, (JCIf)newT, elementBounds);
break;
case EXEC:
retVal = diffExec((JCExpressionStatement)oldT, (JCExpressionStatement)newT, elementBounds);
break;
case BREAK:
retVal = diffBreak((JCBreak)oldT, (JCBreak)newT, elementBounds);
break;
case CONTINUE:
retVal = diffContinue((JCContinue)oldT, (JCContinue)newT, elementBounds);
break;
case RETURN:
retVal = diffReturn((JCReturn)oldT, (JCReturn)newT, elementBounds);
break;
case THROW:
retVal = diffThrow((JCThrow)oldT, (JCThrow)newT,elementBounds);
break;
case ASSERT:
retVal = diffAssert((JCAssert)oldT, (JCAssert)newT, elementBounds);
break;
case APPLY:
retVal = diffApply((JCMethodInvocation)oldT, (JCMethodInvocation)newT, elementBounds);
break;
case NEWCLASS:
retVal = diffNewClass((JCNewClass)oldT, (JCNewClass)newT, elementBounds);
break;
case NEWARRAY:
retVal = diffNewArray((JCNewArray)oldT, (JCNewArray)newT, elementBounds);
break;
case PARENS:
retVal = diffParens((JCParens)oldT, (JCParens)newT, elementBounds);
break;
case ASSIGN:
retVal = diffAssign((JCAssign)oldT, (JCAssign)newT, parent, elementBounds);
break;
case TYPECAST:
retVal = diffTypeCast((JCTypeCast)oldT, (JCTypeCast)newT, elementBounds);
break;
case TYPETEST:
retVal = diffTypeTest((JCInstanceOf)oldT, (JCInstanceOf)newT, elementBounds);
break;
case INDEXED:
retVal = diffIndexed((JCArrayAccess)oldT, (JCArrayAccess)newT, elementBounds);
break;
case SELECT:
retVal = diffSelect((JCFieldAccess)oldT, (JCFieldAccess)newT, elementBounds);
break;
case IDENT:
retVal = diffIdent((JCIdent)oldT, (JCIdent)newT, elementBounds);
break;
case LITERAL:
retVal = diffLiteral((JCLiteral)oldT, (JCLiteral)newT, elementBounds);
break;
case TYPEIDENT:
retVal = diffTypeIdent((JCPrimitiveTypeTree)oldT, (JCPrimitiveTypeTree)newT, elementBounds);
break;
case TYPEARRAY:
retVal = diffTypeArray((JCArrayTypeTree)oldT, (JCArrayTypeTree)newT, elementBounds);
break;
case TYPEAPPLY:
retVal = diffTypeApply((JCTypeApply)oldT, (JCTypeApply)newT, elementBounds);
break;
case TYPEPARAMETER:
retVal = diffTypeParameter((JCTypeParameter)oldT, (JCTypeParameter)newT, elementBounds);
break;
case WILDCARD:
retVal = diffWildcard((JCWildcard)oldT, (JCWildcard)newT, elementBounds);
break;
case TYPEBOUNDKIND:
retVal = diffTypeBoundKind((TypeBoundKind)oldT, (TypeBoundKind)newT, elementBounds);
break;
case ANNOTATION: case TYPE_ANNOTATION:
retVal = diffAnnotation((JCAnnotation)oldT, (JCAnnotation)newT, elementBounds);
break;
case LETEXPR:
diffLetExpr((LetExpr)oldT, (LetExpr)newT);
break;
case POS:
case NEG:
case NOT:
case COMPL:
case PREINC:
case PREDEC:
case POSTINC:
case POSTDEC:
case NULLCHK:
retVal = diffUnary((JCUnary)oldT, (JCUnary)newT, elementBounds);
break;
case OR:
case AND:
case BITOR:
case BITXOR:
case BITAND:
case EQ:
case NE:
case LT:
case GT:
case LE:
case GE:
case SL:
case SR:
case USR:
case PLUS:
case MINUS:
case MUL:
case DIV:
case MOD:
retVal = diffBinary((JCBinary)oldT, (JCBinary)newT, elementBounds);
break;
case BITOR_ASG:
case BITXOR_ASG:
case BITAND_ASG:
case SL_ASG:
case SR_ASG:
case USR_ASG:
case PLUS_ASG:
case MINUS_ASG:
case MUL_ASG:
case DIV_ASG:
case MOD_ASG:
retVal = diffAssignop((JCAssignOp)oldT, (JCAssignOp)newT, elementBounds);
break;
case ERRONEOUS:
diffErroneous((JCErroneous)oldT, (JCErroneous)newT, elementBounds);
break;
case MODIFIERS:
retVal = diffModifiers((JCModifiers) oldT, (JCModifiers) newT, parent, elementBounds[0]);
copyTo(retVal, elementBounds[1]);
break;
case TYPEUNION:
retVal = diffUnionType((JCTypeUnion) oldT, (JCTypeUnion) newT, elementBounds);
break;
case LAMBDA:
retVal = diffLambda((JCLambda) oldT, (JCLambda) newT, elementBounds);
break;
case REFERENCE:
retVal = diffMemberReference((JCMemberReference) oldT, (JCMemberReference) newT, elementBounds);
break;
case ANNOTATED_TYPE:
retVal = diffAnnotatedType((JCAnnotatedType)oldT, (JCAnnotatedType)newT, elementBounds);
break;
default:
// handle special cases like field groups and enum constants
if (oldT.getKind() == Kind.OTHER) {
if (oldT instanceof FieldGroupTree) {
return diffFieldGroup((FieldGroupTree) oldT, (FieldGroupTree) newT, elementBounds);
}
break;
}
if(oldT.getKind().toString().equals(TreeShims.SWITCH_EXPRESSION)){
retVal = diffSwitchExpression(oldT, newT, elementBounds);
break;
}
String msg = "Diff not implemented: " +
((com.sun.source.tree.Tree)oldT).getKind().toString() +
" " + oldT.getClass().getName();
throw new AssertionError(msg);
}
if (handleImplicitLambda) {
printer.print(")");
}
if (!innerCommentsProcessed) {
retVal = diffInnerComments(oldT, newT, retVal);
}
int endComment = getCommentCorrectedEndPos(oldT);
return diffTrailingComments(oldT, newT, retVal, endComment);
}
/**
* Three sets representing different kind which can be matched. No need
* to rewrite whole expression. Ensure that CompoundAssignementTrees,
* UnaryTrees and BinaryTrees are matched, i.e. diff method is used
* instead of priting whole new tree.
*/
private static final EnumSet<Kind> compAssign = EnumSet.of(
Kind.MULTIPLY_ASSIGNMENT,
Kind.DIVIDE_ASSIGNMENT,
Kind.REMAINDER_ASSIGNMENT,
Kind.PLUS_ASSIGNMENT,
Kind.MINUS_ASSIGNMENT,
Kind.LEFT_SHIFT_ASSIGNMENT,
Kind.RIGHT_SHIFT_ASSIGNMENT,
Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT,
Kind.AND_ASSIGNMENT,
Kind.XOR_ASSIGNMENT,
Kind.OR_ASSIGNMENT
);
private static final EnumSet<Kind> binaries = EnumSet.of(
Kind.MULTIPLY,
Kind.DIVIDE,
Kind.REMAINDER,
Kind.PLUS,
Kind.MINUS,
Kind.LEFT_SHIFT,
Kind.RIGHT_SHIFT,
Kind.UNSIGNED_RIGHT_SHIFT,
Kind.LESS_THAN,
Kind.GREATER_THAN,
Kind.LESS_THAN_EQUAL,
Kind.GREATER_THAN_EQUAL,
Kind.EQUAL_TO,
Kind.NOT_EQUAL_TO,
Kind.AND,
Kind.XOR,
Kind.OR,
Kind.CONDITIONAL_AND,
Kind.CONDITIONAL_OR
);
private static final EnumSet<Kind> unaries = EnumSet.of(
Kind.POSTFIX_INCREMENT,
Kind.POSTFIX_DECREMENT,
Kind.PREFIX_INCREMENT,
Kind.PREFIX_DECREMENT,
Kind.UNARY_PLUS,
Kind.UNARY_MINUS,
Kind.BITWISE_COMPLEMENT,
Kind.LOGICAL_COMPLEMENT
);
protected boolean listsMatch(List<? extends JCTree> oldList, List<? extends JCTree> newList) {
if (oldList == newList)
return true;
int n = oldList.size();
if (newList.size() != n)
return false;
for (int i = 0; i < n; i++)
if (!treesMatch(oldList.get(i), newList.get(i)))
return false;
return true;
}
private boolean matchImport(JCImport t1, JCImport t2) {
return t1.staticImport == t2.staticImport && treesMatch(t1.qualid, t2.qualid);
}
private boolean matchBlock(JCBlock t1, JCBlock t2) {
return t1.flags == t2.flags && listsMatch(t1.stats, t2.stats);
}
private boolean matchDoLoop(JCDoWhileLoop t1, JCDoWhileLoop t2) {
return treesMatch(t1.cond, t2.cond) && treesMatch(t1.body, t2.body);
}
private boolean matchWhileLoop(JCWhileLoop t1, JCWhileLoop t2) {
return treesMatch(t1.cond, t2.cond) && treesMatch(t1.body, t2.body);
}
private boolean matchForLoop(JCForLoop t1, JCForLoop t2) {
return listsMatch(t1.init, t2.init) && treesMatch(t1.cond, t2.cond) &&
listsMatch(t1.step, t2.step) && treesMatch(t1.body, t2.body);
}
private boolean matchForeachLoop(JCEnhancedForLoop t1, JCEnhancedForLoop t2) {
return treesMatch(t1.var, t2.var) && treesMatch(t1.expr, t2.expr) &&
treesMatch(t1.body, t2.body);
}
private boolean matchLabelled(JCLabeledStatement t1, JCLabeledStatement t2) {
return t1.label == t2.label && treesMatch(t1.body, t2.body);
}
private boolean matchSwitch(JCSwitch t1, JCSwitch t2) {
return treesMatch(t1.selector, t2.selector) && listsMatch(t1.cases, t2.cases);
}
private boolean matchCase(JCCase t1, JCCase t2) {
return treesMatch(t1.pat, t2.pat) && listsMatch(t1.stats, t2.stats);
}
private boolean matchSynchronized(JCSynchronized t1, JCSynchronized t2) {
return treesMatch(t1.lock, t2.lock) && treesMatch(t1.body, t2.body);
}
private boolean matchTry(JCTry t1, JCTry t2) {
return treesMatch(t1.finalizer, t2.finalizer) &&
listsMatch(t1.catchers, t2.catchers) &&
treesMatch(t1.body, t2.body);
}
private boolean matchCatch(JCCatch t1, JCCatch t2) {
return treesMatch(t1.param, t2.param) && treesMatch(t1.body, t2.body);
}
private boolean matchConditional(JCConditional t1, JCConditional t2) {
return treesMatch(t1.cond, t2.cond) && treesMatch(t1.truepart, t2.truepart) &&
treesMatch(t1.falsepart, t2.falsepart);
}
private boolean matchIf(JCIf t1, JCIf t2) {
return treesMatch(t1.cond, t2.cond) && treesMatch(t1.thenpart, t2.thenpart) &&
treesMatch(t1.elsepart, t2.elsepart);
}
private boolean matchBreak(JCBreak t1, JCBreak t2) {
return t1.label == t2.label && treesMatch(t1.target, t2.target);
}
private boolean matchContinue(JCContinue t1, JCContinue t2) {
return t1.label == t2.label && treesMatch(t1.target, t2.target);
}
private boolean matchAssert(JCAssert t1, JCAssert t2) {
return treesMatch(t1.cond, t2.cond) && treesMatch(t1.detail, t2.detail);
}
private boolean matchApply(JCMethodInvocation t1, JCMethodInvocation t2) {
return t1.varargsElement == t2.varargsElement &&
listsMatch(t1.typeargs, t2.typeargs) &&
treesMatch(t1.meth, t2.meth) &&
listsMatch(t1.args, t2.args);
}
private boolean matchNewClass(JCNewClass t1, JCNewClass t2) {
return t1.constructor == t2.constructor &&
treesMatch(t1.getIdentifier(), t2.getIdentifier()) &&
listsMatch(t1.typeargs, t2.typeargs) &&
listsMatch(t1.args, t2.args) &&
(t1.varargsElement == t2.varargsElement) &&
treesMatch(t1.def, t2.def);
}
private boolean matchNewArray(JCNewArray t1, JCNewArray t2) {
return treesMatch(t1.elemtype, t2.elemtype) &&
listsMatch(t1.dims, t2.dims) && listsMatch(t1.elems, t2.elems);
}
private boolean matchAssign(JCAssign t1, JCAssign t2) {
return treesMatch(t1.lhs, t2.lhs) && treesMatch(t1.rhs, t2.rhs);
}
private boolean matchAssignop(JCAssignOp t1, JCAssignOp t2) {
return t1.operator == t2.operator &&
treesMatch(t1.lhs, t2.lhs) && treesMatch(t1.rhs, t2.rhs);
}
private boolean matchUnary(JCUnary t1, JCUnary t2) {
return t1.operator == t2.operator && treesMatch(t1.arg, t2.arg);
}
private boolean matchBinary(JCBinary t1, JCBinary t2) {
return t1.operator == t2.operator &&
treesMatch(t1.lhs, t2.lhs) && treesMatch(t1.rhs, t2.rhs);
}
private boolean matchTypeCast(JCTypeCast t1, JCTypeCast t2) {
return treesMatch(t1.clazz, t2.clazz) && treesMatch(t1.expr, t2.expr);
}
private boolean matchTypeTest(JCInstanceOf t1, JCInstanceOf t2) {
return treesMatch(getPattern(t1), getPattern(t2)) && treesMatch(t1.expr, t2.expr);
}
private boolean matchIndexed(JCArrayAccess t1, JCArrayAccess t2) {
return treesMatch(t1.indexed, t2.indexed) && treesMatch(t1.index, t2.index);
}
private boolean matchSelect(JCFieldAccess t1, JCFieldAccess t2) {
return treesMatch(t1.selected, t2.selected) && t1.name == t2.name;
}
private boolean matchReference(JCMemberReference t1, JCMemberReference t2) {
return treesMatch(t1.expr, t2.expr) && t1.name == t2.name;
}
private boolean matchLiteral(JCLiteral t1, JCLiteral t2) {
return t1.typetag == t2.typetag && (t1.value == t2.value || (t1.value != null && t1.value.equals(t2.value))) && !(possibleTextBlock(t1, t2));
}
private boolean possibleTextBlock(JCLiteral t1, JCLiteral t2) {
return t1.getKind() == Tree.Kind.STRING_LITERAL && t2.getKind() == Tree.Kind.STRING_LITERAL;
}
private boolean matchTypeApply(JCTypeApply t1, JCTypeApply t2) {
return treesMatch(t1.clazz, t2.clazz) &&
listsMatch(t1.arguments, t2.arguments);
}
private boolean matchAnnotatedType(JCAnnotatedType t1, JCAnnotatedType t2) {
return treesMatch(t1.underlyingType, t2.underlyingType) &&
listsMatch(t1.annotations, t2.annotations);
}
private boolean matchTypeParameter(JCTypeParameter t1, JCTypeParameter t2) {
return t1.name == t2.name && listsMatch(t1.bounds, t2.bounds);
}
private boolean matchWildcard(JCWildcard t1, JCWildcard t2) {
return t1.kind == t2.kind && treesMatch(t1.inner, t2.inner);
}
private boolean matchAnnotation(JCAnnotation t1, JCAnnotation t2) {
return treesMatch(t1.annotationType, t2.annotationType) &&
listsMatch(t1.args, t2.args);
}
private boolean matchModifiers(JCModifiers t1, JCModifiers t2) {
return t1.flags == t2.flags && listsMatch(t1.annotations, t2.annotations);
}
private boolean matchLetExpr(LetExpr t1, LetExpr t2) {
return listsMatch(t1.defs, t2.defs) && treesMatch(t1.expr, t2.expr);
}
private boolean matchLambda(JCLambda t1, JCLambda t2) {
return listsMatch(t1.params, t2.params) && treesMatch(t1.body, t2.body);
}
private boolean isCommaSeparated(JCVariableDecl oldT) {
if (getOldPos(oldT) <= 0 || oldT.pos <= 0) {
return false;
}
tokenSequence.move(oldT.pos);
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
if (tokenSequence.token() == null) {
return false;
}
if (JavaTokenId.COMMA == tokenSequence.token().id()) {
return true;
}
if (oldT.getInitializer() != null && (oldT.mods.flags & Flags.ENUM) == 0) {
tokenSequence.move(endPos(oldT.getInitializer()));
} else {
tokenSequence.move(oldT.pos);
tokenSequence.moveNext();
}
moveToSrcRelevant(tokenSequence, Direction.FORWARD);
if (tokenSequence.token() == null) {
return false;
}
if (JavaTokenId.COMMA == tokenSequence.token().id()) {
return true;
}
return false;
}
private int getCommentCorrectedOldPos(JCTree tree) {
CommentSet ch = comments.getComments(tree);
return Math.min(getOldPos(tree), commentStart(diffContext, ch, CommentSet.RelativePosition.PRECEDING, getOldPos(tree)));
}
private int getCommentCorrectedEndPos(JCTree tree) {
final int[] res = new int[] {endPos(tree)};
new ErrorAwareTreeScanner<Void, Void>() {
@Override public Void scan(Tree node, Void p) {
if (node != null) {
CommentSet ch = comments.getComments(node);
res[0] = Math.max(res[0], Math.max(commentEnd(ch, CommentSet.RelativePosition.INLINE), commentEnd(ch, CommentSet.RelativePosition.TRAILING)));
}
return super.scan(node, p);
}
}.scan(tree, null);
return res[0];
}
private int[] getCommentCorrectedBounds(JCTree tree) {
return new int[] {
getCommentCorrectedOldPos(tree),
getCommentCorrectedEndPos(tree)
};
}
private int[] getBounds(JCTree tree) {
return new int[] { getOldPos(tree), endPos(tree) };
}
private int[] getBounds(DCTree tree, DCDocComment doc) {
return new int[] { getOldPos(tree, doc), endPos(tree, doc) };
}
private int copyUpTo(int from, int to, VeryPretty printer) {
if (from < to) {
copyTo(from, to, printer);
return to;
} else {
return from;
}
}
private int copyUpTo(int from, int to) {
return copyUpTo(from, to, printer);
}
private void copyTo(int from, int to) {
copyTo(from, to, printer);
}
public static boolean noInvalidCopyTos = false;
public void copyTo(int from, int to, VeryPretty loc) {
if (from == to) {
return;
} else if (from > to || from < 0 || to < 0) {
// #104107 - log the source when this problem occurs.
LOG.log(INFO, "-----\n" + origText + "-----\n");
LOG.log(INFO, "Illegal values: from = " + from + "; to = " + to + "." +
"Please, attach your messages.log to new issue!");
if (noInvalidCopyTos)
throw new IllegalStateException("Illegal values: from = " + from + "; to = " + to + ".");
if (to >= 0)
printer.eatChars(from-to);
return;
} else if (to > origText.length()) {
// #99333, #97801: Debug message for the issues.
LOG.severe("-----\n" + origText + "-----\n");
throw new IllegalArgumentException("Copying to " + to + " is greater then its size (" + origText.length() + ").");
}
// lazy init
if (boundaries == null) {
boundaries = diffContext.blockSequences.getBoundaries();
}
if (nextBlockBoundary == -1 && boundaries.hasNext()) {
nextBlockBoundary = boundaries.next();
}
while (nextBlockBoundary != -1 && nextBlockBoundary < from) {
if (boundaries.hasNext()) {
nextBlockBoundary = boundaries.next();
} else {
nextBlockBoundary = -1;
break;
}
}
// map the boundary if the copied text starts at OR ends at the boundary. E.g. the after-boundary text might be
// generated, but the boundary itself is still preserved.
while (from <= nextBlockBoundary && to >= nextBlockBoundary) {
int off = nextBlockBoundary - from;
int mapped = loc.out.length() + (from < nextBlockBoundary ? off : 0);
Integer prev = blockSequenceMap.put(nextBlockBoundary, mapped);
if (prev != null) {
// the first recorded value holds.
blockSequenceMap.put(nextBlockBoundary, prev);
}
nextBlockBoundary = boundaries.hasNext() ? boundaries.next() : -1;
}
loc.print(origText.substring(from, to));
}
// temporary method
private int diffTree(JCTree oldT, JCTree newT, int[] elementBounds, Kind parentKind) {
return diffTree(oldT, newT, elementBounds, parentKind, true);
}
/**
* This form contains a special hack for if, so that `else' can be placed on the same
* line as the statement block end.
* If a block statement is generated instead of a single stat, a newline is appended unless `retainNewline'
* is false. If statement print passes false when else part of the if is present.
*
* @param oldT old tree
* @param newT new tree
* @param elementBounds the old element bounds
* @param parentKind parent statement's kind
* @param retainNewline retain newline if block is generated
* @return localpointer value
*/
private int diffTree(JCTree oldT, JCTree newT, int[] elementBounds, Kind parentKind, boolean retainNewline) {
if (oldT.getKind() != newT.getKind() && newT.getKind() == Kind.BLOCK) {
tokenSequence.move(getOldPos(oldT));
moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
tokenSequence.moveNext();
if (elementBounds[0] < tokenSequence.offset())
copyTo(elementBounds[0], tokenSequence.offset());
printer.printBlock(oldT, newT, parentKind);
// ensure there's a newline if the replaced command ended with one.
int localpointer = getCommentCorrectedEndPos(oldT);
if (retainNewline && localpointer > 0 && origText.charAt(localpointer - 1) == '\n') { // NOI18N
printer.newline();
}
return localpointer;
} else {
// next statement can to seem redundant, but is not, see 117774
copyTo(elementBounds[0], elementBounds[0] = getCommentCorrectedBounds(oldT)[0]);
return diffTree(oldT, newT, elementBounds);
}
}
// ---- TreeDiff inner classes - need refactoring.
public static enum DiffTypes {
/**
* The tree has been modified; that is, different versions
* of it exist in the old and new parent trees.
*/
MODIFY("modify"),
/**
* The tree is an insertion; that is, it exists in the
* new tree, but not the old.
*/
INSERT("insert"),
/**
* The tree was deleted; which means that it exists in the
* old parent tree, but not the new one.
*/
DELETE("delete");
DiffTypes(String name) {
this.name = name;
}
public final String name;
}
public static enum LineInsertionType {
BEFORE, AFTER, NONE
}
public static class Diff {
public DiffTypes type;
int pos;
int endOffset;
protected JCTree oldTree;
protected JCTree newTree;
protected Comment oldComment;
protected Comment newComment;
private String text;
boolean trailing;
public static Diff insert(int pos, String text) {
return new Diff(DiffTypes.INSERT, pos, Position.NOPOS /* does not matter */, text);
}
public static Diff delete(int startOffset, int endOffset) {
return new Diff(DiffTypes.DELETE, startOffset, endOffset, null);
}
Diff(DiffTypes type, int pos, int endOffset, String text) {
this.type = type;
this.pos = pos;
this.endOffset = endOffset;
this.text = text;
}
Diff(DiffTypes type, int pos, JCTree oldTree, JCTree newTree,
Comment oldComment, Comment newComment, boolean trailing) {
this(type, pos, -1, null);
assert pos >= 0 : "invalid source offset";
this.oldTree = oldTree;
this.newTree = newTree;
this.oldComment = oldComment;
this.newComment = newComment;
this.trailing = trailing;
}
public JCTree getOld() {
return oldTree;
}
public JCTree getNew() {
return newTree;
}
public int getPos() {
return pos;
}
public int getEnd() {
return endOffset;
}
public String getText() {
return text;
}
public Comment getOldComment() {
return oldComment;
}
public Comment getNewComment() {
return newComment;
}
public boolean isTrailingComment() {
return trailing;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Diff))
return false;
Diff d2 = (Diff)obj;
return type != d2.type &&
pos != d2.pos &&
oldTree != d2.oldTree &&
newTree != d2.newTree &&
oldComment != d2.oldComment &&
newComment != d2.newComment &&
trailing != d2.trailing;
}
@Override
public int hashCode() {
return type.hashCode() + pos +
(oldTree != null ? oldTree.hashCode() : 0) +
(newTree != null ? newTree.hashCode() : 0) +
(oldComment != null ? oldComment.hashCode() : 0) +
(newComment != null ? newComment.hashCode() : 0) +
Boolean.valueOf(trailing).hashCode();
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("tree (");
sb.append(type.toString());
sb.append(") pos=");
sb.append(pos);
sb.append(", end=").append(endOffset);
if (trailing)
sb.append(" trailing comment");
sb.append("\n");
if (type == DiffTypes.DELETE || type == DiffTypes.INSERT || type == DiffTypes.MODIFY)
addDiffString(sb, oldTree, newTree);
else
addDiffString(sb, oldComment, newComment);
return sb.toString();
}
private void addDiffString(StringBuffer sb, Object o1, Object o2) {
if (o1 != null) {
sb.append("< ");
sb.append(o1.toString());
sb.append((o2 != null) ? "\n---\n> " : "\n");
} else
sb.append("> ");
if (o2 != null) {
sb.append(o2.toString());
sb.append('\n');
}
}
}
private static String printCodeStyle(CodeStyle style) {
if (style == null) {
return "<none>"; // NOI18N
}
StringBuilder sb = new StringBuilder();
try {
Method[] arr = style.getClass().getMethods();
for (Method m : arr) {
if (java.lang.reflect.Modifier.isPublic(m.getModifiers()) &&
(m.getParameterTypes().length == 0)) {
String s = m.getName();
if (s.startsWith("get")) { // NOI18N
s = s.substring(3);
} else if (s.startsWith("is")) { // NOI18N
s = s.substring(2);
}
Object val= m.invoke(style);
if (val instanceof Object[]) {
val = Arrays.asList((Object[])val);
}
sb.append(s).append(":").append(val).append("\n"); // NOI18N
}
}
} catch (Exception ex) {}
return sb.toString();
}
private int findVar(int start, int end) {
tokenSequence.move(end);
while (tokenSequence.movePrevious() && tokenSequence.offset() >= start) {
JavaTokenId token = tokenSequence.token().id();
if (token == JavaTokenId.VAR) {
return tokenSequence.offset();
}
}
return -1;
}
}