advanced tree diff which does not replace the whole node if there are many children inserted/removed. it's critical for formatter which should not invalidate composite elements but just replace whitespaces instead

This commit is contained in:
Alexey Kudravtsev
2009-10-16 11:08:57 +04:00
parent b4eb8e33db
commit 27389fa498
13 changed files with 149 additions and 188 deletions
@@ -30,7 +30,6 @@ import com.intellij.pom.impl.PomTransactionBase;
import com.intellij.pom.tree.TreeAspect;
import com.intellij.pom.tree.TreeAspectEvent;
import com.intellij.pom.tree.events.TreeChangeEvent;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.PsiFileImpl;
@@ -58,11 +57,7 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
/**
* Created by IntelliJ IDEA.
* User: max
* Date: Jan 21, 2005
* Time: 3:30:29 PM
* To change this template use File | Settings | File Templates.
*/
public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
private static final Logger LOG = Logger.getInstance("#com.intellij.lang.impl.PsiBuilderImpl");
@@ -110,7 +105,6 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
@NonNls private static final String UNBALANCED_MESSAGE =
"Unbalanced tree. Most probably caused by unbalanced markers. Try calling setDebugMode(true) against PsiBuilder passed to identify exact location of the problem";
private PsiElement myInjectionHost;
private ITokenTypeRemapper myRemapper;
public static void registerWhitespaceToken(IElementType type) {
@@ -128,7 +122,6 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
myCharTable = SharedImplUtil.findCharTableByTree(chameleon);
myOriginalTree = chameleon.getUserData(BlockSupport.TREE_TO_BE_REPARSED);
myInjectionHost = chameleon.getPsi().getContext();
myFileLevelParsing = myCharTable == null || myOriginalTree != null;
cacheLexems();
@@ -158,9 +151,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
myLexStarts = new int[approxLexCount];
myLexTypes = new IElementType[approxLexCount];
int i = 0;
myLexer.start(myText);
int i = 0;
while (true) {
IElementType type = myLexer.getTokenType();
if (type == null) break;
@@ -183,12 +175,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
myComments = tokens;
}
@Nullable
public PsiElement getInjectionHost() {
return myInjectionHost;
}
private static abstract class Node implements LighterASTNode {
private abstract static class Node implements LighterASTNode {
public abstract int hc();
}
@@ -524,8 +511,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
}
private StartMarker createMarker(final int lexemIndex) {
StartMarker marker;
marker = START_MARKERS.alloc();
StartMarker marker = START_MARKERS.alloc();
marker.myLexemIndex = lexemIndex;
marker.myBuilder = this;
@@ -701,15 +687,15 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
myConvertor = new ASTConvertor((Node)rootNode);
}
public void nodeDeleted(final ASTNode oldParent, final ASTNode oldNode) {
public void nodeDeleted(@NotNull final ASTNode oldParent, @NotNull final ASTNode oldNode) {
myDelegate.nodeDeleted(oldParent, oldNode);
}
public void nodeInserted(final ASTNode oldParent, final LighterASTNode newNode, final int pos) {
public void nodeInserted(@NotNull final ASTNode oldParent, @NotNull final LighterASTNode newNode, final int pos) {
myDelegate.nodeInserted(oldParent, myConvertor.convert((Node)newNode), pos);
}
public void nodeReplaced(final ASTNode oldChild, final LighterASTNode newChild) {
public void nodeReplaced(@NotNull final ASTNode oldChild, @NotNull final LighterASTNode newChild) {
myDelegate.nodeReplaced(oldChild, myConvertor.convert((Node)newChild));
}
@@ -776,11 +762,10 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
StartMarker curNode = rootMarker;
int lastErrorIndex = -1;
Stack<StartMarker> nodes = new Stack<StartMarker>();
nodes.push(rootMarker);
int lastErrorIndex = -1;
for (int i = 1; i < fProduction.size(); i++) {
ProductionMarker item = fProduction.get(i);
@@ -899,13 +884,20 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
private class MyComparator implements ShallowNodeComparator<ASTNode, LighterASTNode> {
public ThreeState deepEqual(final ASTNode oldNode, final LighterASTNode newNode) {
boolean oldIsErrorElement = oldNode instanceof PsiErrorElement;
boolean newIsErrorElement = newNode.getTokenType() == TokenType.ERROR_ELEMENT;
if (oldIsErrorElement != newIsErrorElement) return ThreeState.NO;
if (oldIsErrorElement && newIsErrorElement) {
final PsiErrorElement e1 = (PsiErrorElement)oldNode;
return Comparing.equal(e1.getErrorDescription(), getErrorMessage(newNode)) ? ThreeState.UNSURE : ThreeState.NO;
}
if (newNode instanceof Token) {
if (oldNode instanceof ForeignLeafPsiElement) {
final IElementType type = newNode.getTokenType();
if (type instanceof ForeignLeafType) {
return ((ForeignLeafType)type).getValue().equals(oldNode.getText()) ? ThreeState.YES : ThreeState.NO;
}
return ThreeState.NO;
return type instanceof ForeignLeafType && ((ForeignLeafType)type).getValue().equals(oldNode.getText())
? ThreeState.YES
: ThreeState.NO;
}
if (oldNode instanceof LeafElement) {
@@ -950,7 +942,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
}
if (n1 instanceof PsiErrorElement && n2.getTokenType() == TokenType.ERROR_ELEMENT) {
final PsiErrorElement e1 = ((PsiErrorElement)n1);
final PsiErrorElement e1 = (PsiErrorElement)n1;
if (!Comparing.equal(e1.getErrorDescription(), getErrorMessage(n2))) return false;
}
@@ -975,10 +967,12 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
myRoot = root;
}
public LighterASTNode prepareForGetChildren(final LighterASTNode o) {
@NotNull
public LighterASTNode prepareForGetChildren(@NotNull final LighterASTNode o) {
return o;
}
@NotNull
public LighterASTNode getRoot() {
return myRoot;
}
@@ -993,7 +987,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
}
private int count;
public int getChildren(final LighterASTNode item, final Ref<LighterASTNode[]> into) {
public int getChildren(@NotNull final LighterASTNode item, @NotNull final Ref<LighterASTNode[]> into) {
if (item instanceof Token || item instanceof ErrorItem) return 0;
StartMarker marker = (StartMarker)item;
@@ -1022,7 +1016,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
into.set(old);
}
else if (count >= old.length) {
LighterASTNode[] newStore = new LighterASTNode[(count * 3) / 2];
LighterASTNode[] newStore = new LighterASTNode[count * 3 / 2];
System.arraycopy(old, 0, newStore, 0, count);
into.set(newStore);
}
@@ -1145,7 +1139,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder {
private void initCachedField() {
try {
cachedElementData = (Object[])ourElementDataField.get(this);
} catch(Exception e) {
}
catch (Exception e) {
LOG.error(e);
}
}
@@ -68,11 +68,11 @@ public class TreeChangeEventImpl implements TreeChangeEvent{
}
public void addElementaryChange(ASTNode element, ChangeInfo change) {
int depth = 0;
final ASTNode parent = element.getTreeParent();
if(parent == null) return;
ASTNode currentParent = parent;
ASTNode prevParent = element;
int depth = 0;
while(currentParent != null){
if(myChangedElements.containsKey(currentParent)){
final TreeChange changesByElement = getChangesByElement(currentParent);
@@ -33,6 +33,7 @@ import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.util.diff.DiffTreeChangeBuilder;
import org.jetbrains.annotations.NotNull;
public class ASTDiffBuilder implements DiffTreeChangeBuilder<ASTNode, ASTNode> {
private final TreeChangeEventImpl myEvent;
@@ -48,7 +49,7 @@ public class ASTDiffBuilder implements DiffTreeChangeBuilder<ASTNode, ASTNode> {
myEvent = new TreeChangeEventImpl(PomManager.getModel(fileImpl.getProject()).getModelAspect(TreeAspect.class), fileImpl.getTreeElement());
}
public void nodeReplaced(ASTNode oldNode, ASTNode newNode) {
public void nodeReplaced(@NotNull ASTNode oldNode, @NotNull ASTNode newNode) {
if (oldNode instanceof FileElement && newNode instanceof FileElement) {
BlockSupportImpl.replaceFileElement(myFile, (FileElement)oldNode, (FileElement)newNode, myPsiManager);
}
@@ -67,7 +68,6 @@ public class ASTDiffBuilder implements DiffTreeChangeBuilder<ASTNode, ASTNode> {
if (!(newNode instanceof FileElement)) {
((CompositeElement)newNode.getTreeParent()).subtreeChanged();
}
//System.out.println("REPLACED: " + oldNode + " to " + newNode);
}
}
@@ -83,13 +83,12 @@ public class ASTDiffBuilder implements DiffTreeChangeBuilder<ASTNode, ASTNode> {
}
}
public void nodeDeleted(ASTNode parent, final ASTNode child) {
public void nodeDeleted(@NotNull ASTNode parent, @NotNull final ASTNode child) {
PsiElement psiParent = parent.getPsi();
PsiElement psiChild = myIsPhysicalScope ? child.getPsi() : null;
PsiTreeChangeEventImpl event = null;
if (psiParent != null && psiChild != null) {
event = new PsiTreeChangeEventImpl(myPsiManager);
PsiTreeChangeEventImpl event = new PsiTreeChangeEventImpl(myPsiManager);
event.setParent(psiParent);
event.setChild(psiChild);
myPsiManager.beforeChildRemoval(event);
@@ -98,25 +97,14 @@ public class ASTDiffBuilder implements DiffTreeChangeBuilder<ASTNode, ASTNode> {
myEvent.addElementaryChange(child, ChangeInfoImpl.create(ChangeInfo.REMOVED, child));
((TreeElement)child).rawRemove();
((CompositeElement)parent).subtreeChanged();
/*if (event != null) {
myPsiManager.childRemoved(event);
}*/
//System.out.println("DELETED from " + parent + ": " + child);
}
public void nodeInserted(final ASTNode oldParent, ASTNode node, final int pos) {
public void nodeInserted(@NotNull final ASTNode oldParent, @NotNull ASTNode node, final int pos) {
transformNewChameleon(oldParent, node);
ASTNode anchor = null;
for (int i = 0; i < pos; i++) {
if (anchor == null) {
anchor = oldParent.getFirstChildNode();
}
else {
anchor = anchor.getTreeNext();
}
anchor = anchor == null ? oldParent.getFirstChildNode() : anchor.getTreeNext();
}
((TreeElement)node).rawRemove();
@@ -135,8 +123,6 @@ public class ASTDiffBuilder implements DiffTreeChangeBuilder<ASTNode, ASTNode> {
myEvent.addElementaryChange(node, ChangeInfoImpl.create(ChangeInfo.ADD, node));
((TreeElement)node).clearCaches();
((CompositeElement)oldParent).subtreeChanged();
//System.out.println("INSERTED to " + oldParent + ": " + node + " at " + pos);
}
public TreeChangeEventImpl getEvent() {
@@ -40,13 +40,12 @@ public class ASTShallowComparator implements ShallowNodeComparator<ASTNode, ASTN
}
if (oldNode instanceof LeafElement) {
if (newNode instanceof LeafElement) return ((LeafElement)oldNode).textMatches(newNode.getChars()) ? ThreeState.YES : ThreeState.NO;
return ((LeafElement)oldNode).textMatches(newNode.getText()) ? ThreeState.YES : ThreeState.NO;
}
if (oldNode instanceof PsiErrorElement && newNode instanceof PsiErrorElement) {
final PsiErrorElement e1 = ((PsiErrorElement)oldNode);
final PsiErrorElement e2 = ((PsiErrorElement)newNode);
final PsiErrorElement e1 = (PsiErrorElement)oldNode;
final PsiErrorElement e2 = (PsiErrorElement)newNode;
if (!Comparing.equal(e1.getErrorDescription(), e2.getErrorDescription())) return ThreeState.NO;
}
@@ -63,11 +62,12 @@ public class ASTShallowComparator implements ShallowNodeComparator<ASTNode, ASTN
}
if (n1 instanceof PsiErrorElement && n2 instanceof PsiErrorElement) {
final PsiErrorElement e1 = ((PsiErrorElement)n1);
final PsiErrorElement e2 = ((PsiErrorElement)n2);
final PsiErrorElement e1 = (PsiErrorElement)n1;
final PsiErrorElement e2 = (PsiErrorElement)n2;
if (!Comparing.equal(e1.getErrorDescription(), e2.getErrorDescription())) return false;
}
return ((TreeElement)n1).hc() == ((TreeElement)n2).hc();
}
}
@@ -19,6 +19,7 @@ package com.intellij.psi.impl.source.tree;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Ref;
import com.intellij.util.diff.FlyweightCapableTreeStructure;
import org.jetbrains.annotations.NotNull;
/**
* @author max
@@ -26,14 +27,16 @@ import com.intellij.util.diff.FlyweightCapableTreeStructure;
public class ASTStructure implements FlyweightCapableTreeStructure<ASTNode> {
private final ASTNode myRoot;
public ASTStructure(final ASTNode root) {
public ASTStructure(@NotNull ASTNode root) {
myRoot = root;
}
public ASTNode prepareForGetChildren(final ASTNode astNode) {
@NotNull
public ASTNode prepareForGetChildren(@NotNull final ASTNode astNode) {
return astNode;
}
@NotNull
public ASTNode getRoot() {
return myRoot;
}
@@ -41,7 +44,7 @@ public class ASTStructure implements FlyweightCapableTreeStructure<ASTNode> {
public void disposeChildren(final ASTNode[] nodes, final int count) {
}
public int getChildren(final ASTNode astNode, final Ref<ASTNode[]> into) {
public int getChildren(@NotNull final ASTNode astNode, @NotNull final Ref<ASTNode[]> into) {
ASTNode child = astNode.getFirstChildNode();
if (child == null) return 0;
@@ -30,12 +30,13 @@ public class LazyParseableElement extends CompositeElement {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.LazyParseableElement");
private final Object lock = new String("chameleon parsing lock");
//private final Object lock = PsiLock.LOCK;
private CharSequence myText;
public LazyParseableElement(@NotNull IElementType type, CharSequence text) {
super(type);
myText = text != null ? text.toString() : null;
synchronized (lock) {
myText = text == null ? null : text.toString();
}
}
@NotNull
@@ -152,15 +152,6 @@ public abstract class LeafElement extends TreeElement {
final int len = text.length();
int hc = 0;
if (len > TEXT_MATCHES_THRESHOLD && text instanceof String) {
final String str = (String)text;
for (int i = 0; i < len; i++) {
hc += str.charAt(i);
}
return hc;
}
for (int i = 0; i < len; i++) {
hc += text.charAt(i);
}
@@ -57,7 +57,7 @@ class InjectedFileViewProvider extends SingleRootFileViewProvider {
public void rootChanged(PsiFile psiFile) {
super.rootChanged(psiFile);
if (!isPhysical()) return; // injected PSI change happened
if (!isPhysical()) return; // injected PSI change happened inside reparse; ignore
List<PsiLanguageInjectionHost.Shred> shreds;
synchronized (myLock) {
@@ -620,8 +620,6 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
addGutterIconRenderer(info.getGutterIconRenderer(), info.startOffset);
}
doHighlighting();
for (final RangeHighlighter highlighter : myEditor.getDocument().getMarkupModel(project).getAllHighlighters()) {
addGutterIconRenderer(highlighter.getGutterIconRenderer(), highlighter.getStartOffset());
}
@@ -1063,7 +1061,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
}
@NotNull
private List<HighlightInfo> doHighlighting() {
public List<HighlightInfo> doHighlighting() {
final Project project = myProjectFixture.getProject();
PsiDocumentManager.getInstance(project).commitAllDocuments();
@@ -20,7 +20,6 @@ import com.intellij.openapi.util.Ref;
import com.intellij.util.ThreeState;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@@ -35,9 +34,8 @@ public class DiffTree<OT, NT> {
private final DiffTreeChangeBuilder<OT, NT> myConsumer;
private final List<Ref<OT[]>> myOldChildrenLists = new ArrayList<Ref<OT[]>>();
private final List<Ref<NT[]>> myNewChildrenLists = new ArrayList<Ref<NT[]>>();
private final List<ThreeState[]> myDeepStates = new ArrayList<ThreeState[]>();
public DiffTree(final FlyweightCapableTreeStructure<OT> oldTree,
private DiffTree(final FlyweightCapableTreeStructure<OT> oldTree,
final FlyweightCapableTreeStructure<NT> newTree,
final ShallowNodeComparator<OT, NT> comparator,
final DiffTreeChangeBuilder<OT, NT> consumer) {
@@ -52,6 +50,13 @@ public class DiffTree<OT, NT> {
new DiffTree<OT, NT>(oldTree, newTree, comparator, consumer).build(oldTree.getRoot(), newTree.getRoot(), 0);
}
private static enum CompareResult {
EQUAL, // 100% equal
DRILL_DOWN_NEEDED, // element types are equal, but elements are composite
TYPE_ONLY, // only element types are equal
NOT_EQUAL, // 100% different
}
// TODO: disposeChildren
private void build(OT oldN, NT newN, int level) {
OT oldNode = myOldTree.prepareForGetChildren(oldN);
@@ -63,11 +68,11 @@ public class DiffTree<OT, NT> {
}
final Ref<OT[]> oldChildrenR = myOldChildrenLists.get(level);
final int oldSize = myOldTree.getChildren(oldNode, oldChildrenR);
int oldSize = myOldTree.getChildren(oldNode, oldChildrenR);
final OT[] oldChildren = oldChildrenR.get();
final Ref<NT[]> newChildrenR = myNewChildrenLists.get(level);
final int newSize = myNewTree.getChildren(newNode, newChildrenR);
int newSize = myNewTree.getChildren(newNode, newChildrenR);
final NT[] newChildren = newChildrenR.get();
if (Math.abs(oldSize - newSize) > CHANGE_PARENT_VERSUS_CHILDREN_THRESHOLD) {
@@ -81,120 +86,105 @@ public class DiffTree<OT, NT> {
if (!comparator.hashcodesEqual(oldNode, newNode) || !comparator.typesEqual(oldNode, newNode)) {
myConsumer.nodeReplaced(oldNode, newNode);
}
disposeLevel(oldChildren, oldSize, newChildren, newSize);
return;
}
boolean walkedDeep = false;
while (oldSize > 0 && newSize > 0) {
OT oldChild1 = oldChildren[oldSize-1];
NT newChild1 = newChildren[newSize-1];
ThreeState[] deeps;
if (oldSize == newSize) {
while (myDeepStates.size() <= level) myDeepStates.add(new ThreeState[oldSize]);
deeps = myDeepStates.get(level);
if (deeps.length < oldSize) {
deeps = new ThreeState[oldSize];
myDeepStates.set(level, deeps);
CompareResult c11 = looksEqual(comparator, oldChild1, newChild1);
if (c11 != CompareResult.EQUAL && c11 != CompareResult.DRILL_DOWN_NEEDED) {
break;
}
else {
Arrays.fill(deeps, 0, oldSize, null);
if (c11 == CompareResult.DRILL_DOWN_NEEDED) {
build(oldChild1, newChild1, level + 1);
}
}
else {
deeps = null;
oldSize--;
newSize--;
}
int start = 0;
while (start < oldSize && start < newSize) {
OT oldChild = oldChildren[start];
NT newChild = newChildren[start];
if (!comparator.typesEqual(oldChild, newChild)) break;
final ThreeState dp = comparator.deepEqual(oldChild, newChild);
if (deeps != null) deeps[start] = dp;
int oldIndex = 0;
int newIndex = 0;
while (oldIndex < oldSize || newIndex < newSize) {
OT oldChild1 = oldIndex < oldSize ? oldChildren[oldIndex] : null;
OT oldChild2 = oldIndex < oldSize-1 ? oldChildren[oldIndex+1] : null;
NT newChild1 = newIndex < newSize ? newChildren[newIndex] : null;
NT newChild2 = newIndex < newSize-1 ? newChildren[newIndex+1] : null;
if (dp != ThreeState.YES) {
if (!comparator.hashcodesEqual(oldChild, newChild)) break;
if (dp == ThreeState.UNSURE) {
build(oldChild, newChild, level + 1);
walkedDeep = true;
}
else if (dp == ThreeState.NO) {
myConsumer.nodeReplaced(oldChild, newChild);
CompareResult c11 = looksEqual(comparator, oldChild1, newChild1);
if (c11 == CompareResult.EQUAL || c11 == CompareResult.DRILL_DOWN_NEEDED) {
if (c11 == CompareResult.DRILL_DOWN_NEEDED) {
build(oldChild1, newChild1, level+1);
}
oldIndex++;
newIndex++;
continue;
}
start++;
}
int oldEnd = oldSize - 1;
int newEnd = newSize - 1;
if (oldSize == newSize && start == newSize) {
disposeLevel(oldChildren, oldSize, newChildren, newSize);
return; // No changes at all at this level
}
while (oldEnd >= start && newEnd >= start) {
OT oldChild = oldChildren[oldEnd];
NT newChild = newChildren[newEnd];
if (!comparator.typesEqual(oldChild, newChild)) break;
final ThreeState dp = comparator.deepEqual(oldChild, newChild);
if (deeps != null) deeps[oldEnd] = dp;
if (dp != ThreeState.YES) {
if (!comparator.hashcodesEqual(oldChild, newChild)) break;
if (dp == ThreeState.UNSURE) {
build(oldChild, newChild, level + 1);
walkedDeep = true;
CompareResult c12 = looksEqual(comparator, oldChild1, newChild2);
CompareResult c21 = looksEqual(comparator, oldChild2, newChild1);
if (c11 == CompareResult.TYPE_ONLY) {
if (c21 == CompareResult.EQUAL || c21 == CompareResult.DRILL_DOWN_NEEDED) {
myConsumer.nodeDeleted(oldNode, oldChild1);
oldIndex++;
continue;
}
else if (dp == ThreeState.NO) {
myConsumer.nodeReplaced(oldChild, newChild);
}
}
oldEnd--;
newEnd--;
}
if (oldSize == newSize) {
for (int i = start; i <= newEnd; i++) {
final OT oldChild = oldChildren[i];
final NT newChild = newChildren[i];
if (comparator.typesEqual(oldChild, newChild)) {
final ThreeState de = deeps[i];
if (de == ThreeState.UNSURE) {
build(oldChild, newChild, level + 1);
}
else if (de == ThreeState.NO || de == null) {
myConsumer.nodeReplaced(oldChild, newChild);
}
else if (c12 == CompareResult.EQUAL || c12 == CompareResult.DRILL_DOWN_NEEDED) {
myConsumer.nodeInserted(oldNode, newChild1, newIndex);
newIndex++;
continue;
}
else {
myConsumer.nodeReplaced(oldChild, newChild);
myConsumer.nodeReplaced(oldChild1, newChild1);
oldIndex++;
newIndex++;
continue;
}
}
}
else {
if (!walkedDeep && start == 0 && newEnd == newSize - 1 && oldEnd == oldSize - 1 && start < oldEnd && start < newEnd) {
myConsumer.nodeReplaced(oldNode, newNode);
disposeLevel(oldChildren, oldSize, newChildren, newSize);
return;
if (c12 == CompareResult.EQUAL || c12 == CompareResult.DRILL_DOWN_NEEDED || c12 == CompareResult.TYPE_ONLY) {
myConsumer.nodeInserted(oldNode, newChild1, newIndex);
newIndex++;
continue;
}
for (int i = start; i <= oldEnd; i++) {
final OT oldChild = oldChildren[i];
myConsumer.nodeDeleted(oldNode, oldChild);
if (c21 == CompareResult.EQUAL || c21 == CompareResult.DRILL_DOWN_NEEDED || c21 == CompareResult.TYPE_ONLY) {
myConsumer.nodeDeleted(oldNode, oldChild1);
oldIndex++;
continue;
}
for (int i = start; i <= newEnd; i++) {
myConsumer.nodeInserted(oldNode, newChildren[i], i);
if (oldChild1 == null) {
myConsumer.nodeInserted(oldNode, newChild1, newIndex);
newIndex++;
continue;
}
if (newChild1 == null) {
myConsumer.nodeDeleted(oldNode, oldChild1);
oldIndex++;
continue;
}
myConsumer.nodeReplaced(oldChild1, newChild1);
oldIndex++;
newIndex++;
}
disposeLevel(oldChildren, oldSize, newChildren, newSize);
}
private CompareResult looksEqual(ShallowNodeComparator<OT, NT> comparator, OT oldChild1, NT newChild1) {
if (oldChild1 == null || newChild1 == null) {
return oldChild1 == newChild1 ? CompareResult.EQUAL : CompareResult.NOT_EQUAL;
}
if (!comparator.typesEqual(oldChild1, newChild1)) return CompareResult.NOT_EQUAL;
ThreeState ret = comparator.deepEqual(oldChild1, newChild1);
if (ret == ThreeState.UNSURE) return CompareResult.DRILL_DOWN_NEEDED;
if (ret == ThreeState.YES) return CompareResult.EQUAL;
return CompareResult.TYPE_ONLY;
}
private void disposeLevel(final OT[] oldChildren, final int oldSize, final NT[] newChildren, final int newSize) {
myOldTree.disposeChildren(oldChildren, oldSize);
myNewTree.disposeChildren(newChildren, newSize);
@@ -16,11 +16,13 @@
package com.intellij.util.diff;
import org.jetbrains.annotations.NotNull;
/**
* @author max
*/
public interface DiffTreeChangeBuilder<OT, NT> {
void nodeReplaced(OT oldChild, NT newChild);
void nodeDeleted(OT oldParent, OT oldNode);
void nodeInserted(OT oldParent, NT newNode, int pos);
void nodeReplaced(@NotNull OT oldChild, @NotNull NT newChild);
void nodeDeleted(@NotNull OT oldParent, @NotNull OT oldNode);
void nodeInserted(@NotNull OT oldParent, @NotNull NT newNode, int pos);
}
@@ -17,13 +17,14 @@
package com.intellij.util.diff;
import com.intellij.openapi.util.Ref;
import org.jetbrains.annotations.NotNull;
/**
* @author max
*/
public interface FlyweightCapableTreeStructure<T> {
T prepareForGetChildren(T node);
T getRoot();
int getChildren(T parent, Ref<T[]> into);
@NotNull T prepareForGetChildren(@NotNull T node);
@NotNull T getRoot();
int getChildren(@NotNull T parent, @NotNull Ref<T[]> into);
void disposeChildren(T[] nodes, int count);
}
@@ -18,6 +18,7 @@ package com.intellij.util.diff;
import com.intellij.openapi.util.Ref;
import com.intellij.util.ThreeState;
import junit.framework.TestCase;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,18 +29,10 @@ import java.util.List;
*/
@SuppressWarnings({"HardCodedStringLiteral"})
public class DiffTreeTest extends TestCase {
private static final Node[] EMPTY = new Node[0];
private static class Node {
private final Node[] myChildren;
int myId;
public Node(final int id) {
myChildren = EMPTY;
myId = id;
}
public Node(final int id, Node... children) {
myChildren = children;
myId = id;
@@ -69,10 +62,12 @@ public class DiffTreeTest extends TestCase {
myRoot = root;
}
public Node prepareForGetChildren(final Node node) {
@NotNull
public Node prepareForGetChildren(@NotNull final Node node) {
return node;
}
@NotNull
public Node getRoot() {
return myRoot;
}
@@ -80,7 +75,7 @@ public class DiffTreeTest extends TestCase {
public void disposeChildren(final Node[] nodes, final int count) {
}
public int getChildren(final Node node, final Ref<Node[]> into) {
public int getChildren(@NotNull final Node node, @NotNull final Ref<Node[]> into) {
into.set(node.getChildren());
return into.get().length;
}
@@ -103,15 +98,15 @@ public class DiffTreeTest extends TestCase {
public static class DiffBuilder implements DiffTreeChangeBuilder<Node, Node> {
private final List<String> myResults = new ArrayList<String>();
public void nodeReplaced(final Node oldNode, final Node newNode) {
public void nodeReplaced(@NotNull final Node oldNode, @NotNull final Node newNode) {
myResults.add("REPLACED: " + oldNode + " to " + newNode);
}
public void nodeDeleted(final Node parent, final Node child) {
public void nodeDeleted(@NotNull final Node parent, @NotNull final Node child) {
myResults.add("DELETED from " + parent + ": " + child);
}
public void nodeInserted(final Node oldParent, final Node node, final int pos) {
public void nodeInserted(@NotNull final Node oldParent, @NotNull final Node node, final int pos) {
myResults.add("INSERTED to " + oldParent + ": " + node + " at " + pos);
}
@@ -212,9 +207,8 @@ public class DiffTreeTest extends TestCase {
final List<String> expectedList = Arrays.asList(expected);
final List<String> actual = result.getEvents();
if (expectedList.size() > 0 && actual.size() > 0) {
if (!expectedList.isEmpty() && !actual.isEmpty()) {
assertEquals(expectedList, actual);
}
}
}