encapsulate ImmutableText

This commit is contained in:
Alexey Kudravtsev
2016-07-27 13:41:01 +03:00
parent fd9483f31b
commit ffbe8cbb95
7 changed files with 77 additions and 150 deletions
@@ -38,7 +38,7 @@ import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.ImmutableText;
import com.intellij.util.text.ImmutableCharSequence;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TObjectProcedure;
import org.jetbrains.annotations.NonNls;
@@ -66,7 +66,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
@SuppressWarnings("RedundantStringConstructorCall") private final Object myLineSetLock = new String("line set lock");
private volatile LineSet myLineSet;
private volatile ImmutableText myText;
private volatile ImmutableCharSequence myText;
private volatile SoftReference<String> myTextString;
private volatile FrozenDocument myFrozen;
@@ -133,7 +133,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
public DocumentImpl(@NotNull CharSequence chars, boolean acceptSlashR, boolean forUseInNonAWTThread) {
setAcceptSlashR(acceptSlashR);
assertValidSeparators(chars);
myText = ImmutableText.valueOf(chars);
myText = CharArrayUtil.createImmutableCharSequence(chars);
setCyclicBufferSize(0);
setModificationStamp(LocalTimeCounter.currentTime());
myAssertThreading = !forUseInNonAWTThread;
@@ -472,12 +472,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
RangeMarker marker = getRangeGuard(offset, offset);
if (marker != null) {
throwGuardedFragment(marker, offset, null, s.toString());
throwGuardedFragment(marker, offset, "", s);
}
myText = myText.ensureChunked();
ImmutableText newText = myText.insert(offset, ImmutableText.valueOf(s));
updateText(newText, offset, null, newText.subtext(offset, offset + s.length()), false, LocalTimeCounter.currentTime(), offset, 0);
ImmutableCharSequence newText = myText.insert(offset, s);
ImmutableCharSequence newString = newText.subtext(offset, offset + s.length());
updateText(newText, offset, "", newString, false, LocalTimeCounter.currentTime(), offset, 0);
trimToSize();
}
@@ -497,11 +497,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
RangeMarker marker = getRangeGuard(startOffset, endOffset);
if (marker != null) {
throwGuardedFragment(marker, startOffset, myText.subSequence(startOffset, endOffset).toString(), null);
throwGuardedFragment(marker, startOffset, myText.subSequence(startOffset, endOffset), "");
}
myText = myText.ensureChunked();
updateText(myText.delete(startOffset, endOffset), startOffset, myText.subtext(startOffset, endOffset), null, false, LocalTimeCounter.currentTime(), startOffset, endOffset - startOffset);
ImmutableCharSequence newText = myText.delete(startOffset, endOffset);
ImmutableCharSequence oldString = myText.subtext(startOffset, endOffset);
updateText(newText, startOffset, oldString, "", false, LocalTimeCounter.currentTime(), startOffset, endOffset - startOffset);
}
@Override
@@ -575,15 +576,14 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
CharSequence sToDelete = myText.subtext(startOffset, endOffset);
RangeMarker guard = getRangeGuard(startOffset, endOffset);
if (guard != null) {
throwGuardedFragment(guard, startOffset, sToDelete.toString(), changedPart.toString());
throwGuardedFragment(guard, startOffset, sToDelete, changedPart);
}
ImmutableText newText;
if (wholeTextReplaced && s instanceof ImmutableText) {
newText = (ImmutableText)s;
ImmutableCharSequence newText;
if (wholeTextReplaced && s instanceof ImmutableCharSequence) {
newText = (ImmutableCharSequence)s;
}
else {
myText = myText.ensureChunked();
newText = myText.delete(startOffset, endOffset).insert(startOffset, changedPart);
changedPart = newText.subtext(startOffset, startOffset + changedPart.length());
}
@@ -649,7 +649,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
}
}
private void throwGuardedFragment(@NotNull RangeMarker guard, int offset, String oldString, String newString) {
private void throwGuardedFragment(@NotNull RangeMarker guard, int offset, @NotNull CharSequence oldString, @NotNull CharSequence newString) {
if (myCheckGuardedBlocks > 0 && !myGuardsSuppressed) {
DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, false);
throw new ReadOnlyFragmentModificationException(event, guard);
@@ -698,10 +698,10 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
myFrozen = null;
}
private void updateText(@NotNull ImmutableText newText,
private void updateText(@NotNull ImmutableCharSequence newText,
int offset,
@Nullable CharSequence oldString,
@Nullable CharSequence newString,
@NotNull CharSequence oldString,
@NotNull CharSequence newString,
boolean wholeTextReplaced,
long newModificationStamp,
int initialStartOffset,
@@ -712,7 +712,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced, initialStartOffset, initialOldLength);
doBeforeChangedUpdate(event);
myTextString = null;
ImmutableText prevText = myText;
ImmutableCharSequence prevText = myText;
myText = newText;
sequence.incrementAndGet(); // increment sequence before firing events so that modification sequence on commit will match this sequence now
changedUpdate(event, newModificationStamp, prevText);
@@ -764,7 +764,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
}
}
private void changedUpdate(@NotNull DocumentEvent event, long newModificationStamp, ImmutableText prevText) {
private void changedUpdate(@NotNull DocumentEvent event, long newModificationStamp, @NotNull CharSequence prevText) {
try {
if (LOG.isDebugEnabled()) LOG.debug(event.toString());
@@ -28,7 +28,7 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.reference.SoftReference;
import com.intellij.util.Processor;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.ImmutableText;
import com.intellij.util.text.ImmutableCharSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,12 +40,12 @@ import java.util.List;
* @author peter
*/
public class FrozenDocument implements DocumentEx {
private final ImmutableText myText;
private final ImmutableCharSequence myText;
private final LineSet myLineSet;
private final long myStamp;
private volatile SoftReference<String> myTextString;
public FrozenDocument(@NotNull ImmutableText text, @NotNull LineSet lineSet, long stamp, @Nullable String textString) {
FrozenDocument(@NotNull ImmutableCharSequence text, @NotNull LineSet lineSet, long stamp, @Nullable String textString) {
myText = text;
myLineSet = lineSet;
myStamp = stamp;
@@ -55,8 +55,8 @@ public class FrozenDocument implements DocumentEx {
public FrozenDocument applyEvent(DocumentEvent event, int newStamp) {
final int offset = event.getOffset();
final int oldEnd = offset + event.getOldLength();
final ImmutableText newText = myText.delete(offset, oldEnd).insert(offset, event.getNewFragment());
final LineSet newLineSet = myLineSet.update(myText, offset, oldEnd, event.getNewFragment(), event.isWholeTextReplaced());
ImmutableCharSequence newText = myText.delete(offset, oldEnd).insert(offset, event.getNewFragment());
LineSet newLineSet = myLineSet.update(myText, offset, oldEnd, event.getNewFragment(), event.isWholeTextReplaced());
return new FrozenDocument(newText, newLineSet, newStamp, null);
}
@@ -17,7 +17,6 @@ package com.intellij.openapi.editor.impl.event;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.diff.Diff;
import com.intellij.util.diff.FilesTooBigForDiffException;
import org.jetbrains.annotations.NotNull;
@@ -29,38 +28,27 @@ public class DocumentEventImpl extends DocumentEvent {
private final CharSequence myNewString;
private final int myNewLength;
private boolean isOnlyOneLineChangedCalculated = false;
private boolean isOnlyOneLineChanged;
private boolean isStartOldIndexCalculated = false;
private int myStartOldIndex;
private final long myOldTimeStamp;
private final boolean myIsWholeDocReplaced;
private Diff.Change myChange;
private static final Diff.Change TOO_BIG_FILE = new Diff.Change(0, 0, 0, 0, null) {
};
private int myOptimizedLineShift = -1;
private boolean myOptimizedLineShiftCalculated;
private int myOptimizedOldLineShift = -1;
private boolean myOptimizedOldLineShiftCalculated;
private final int myInitialStartOffset;
private final int myInitialOldLength;
public DocumentEventImpl(@NotNull Document document,
int offset,
CharSequence oldString,
CharSequence newString,
@NotNull CharSequence oldString,
@NotNull CharSequence newString,
long oldTimeStamp,
boolean wholeTextReplaced) {
this(document, offset, oldString, newString, oldTimeStamp, wholeTextReplaced, offset, oldString == null ? 0 : oldString.length());
this(document, offset, oldString, newString, oldTimeStamp, wholeTextReplaced, offset, oldString.length());
}
public DocumentEventImpl(@NotNull Document document,
int offset,
CharSequence oldString,
CharSequence newString,
@NotNull CharSequence oldString,
@NotNull CharSequence newString,
long oldTimeStamp,
boolean wholeTextReplaced,
int initialStartOffset,
@@ -68,25 +56,18 @@ public class DocumentEventImpl extends DocumentEvent {
super(document);
myOffset = offset;
myOldString = oldString == null ? "" : oldString;
myOldLength = myOldString.length();
myOldString = oldString;
myOldLength = oldString.length();
myNewString = newString == null ? "" : newString;
myNewLength = myNewString.length();
myNewString = newString;
myNewLength = newString.length();
myInitialStartOffset = initialStartOffset;
myInitialOldLength = initialOldLength;
myOldTimeStamp = oldTimeStamp;
if (getDocument().getTextLength() == 0) {
isOnlyOneLineChangedCalculated = true;
isOnlyOneLineChanged = false;
myIsWholeDocReplaced = false;
}
else {
myIsWholeDocReplaced = wholeTextReplaced;
}
myIsWholeDocReplaced = getDocument().getTextLength() != 0 && wholeTextReplaced;
}
@Override
@@ -138,44 +119,12 @@ public class DocumentEventImpl extends DocumentEvent {
return myInitialOldLength;
}
public int getStartOldIndex() {
if (isStartOldIndexCalculated) return myStartOldIndex;
isStartOldIndexCalculated = true;
myStartOldIndex = getDocument().getLineNumber(myOffset);
return myStartOldIndex;
}
public boolean isOnlyOneLineChanged() {
if (isOnlyOneLineChangedCalculated) return isOnlyOneLineChanged;
isOnlyOneLineChangedCalculated = true;
isOnlyOneLineChanged = true;
for (int i = 0; i < myOldString.length(); i++) {
if (myOldString.charAt(i) == '\n') {
isOnlyOneLineChanged = false;
break;
}
}
if (isOnlyOneLineChanged) {
for (int i = 0; i < myNewString.length(); i++) {
if (myNewString.charAt(i) == '\n') {
isOnlyOneLineChanged = false;
break;
}
}
}
return isOnlyOneLineChanged;
}
@Override
public long getOldTimeStamp() {
return myOldTimeStamp;
}
@SuppressWarnings({"HardCodedStringLiteral"})
@SuppressWarnings("HardCodedStringLiteral")
public String toString() {
return "DocumentEventImpl[myOffset=" + myOffset + ", myOldLength=" + myOldLength + ", myNewLength=" + myNewLength +
", myOldString='" + myOldString + "', myNewString='" + myNewString + "']" + (isWholeTextReplaced() ? " Whole." : ".");
@@ -229,29 +178,5 @@ public class DocumentEventImpl extends DocumentEvent {
return myChange;
}
public int getOptimizedLineShift() {
if (!myOptimizedLineShiftCalculated) {
myOptimizedLineShiftCalculated = true;
if (myOldLength == 0) {
int lineShift = StringUtil.countNewLines(myNewString);
myOptimizedLineShift = lineShift == 0 ? -1 : lineShift;
}
}
return myOptimizedLineShift;
}
public int getOptimizedOldLineShift() {
if (!myOptimizedOldLineShiftCalculated) {
myOptimizedOldLineShiftCalculated = true;
if (myNewLength == 0) {
int lineShift = StringUtil.countNewLines(myOldString);
myOptimizedOldLineShift = lineShift == 0 ? -1 : lineShift;
}
}
return myOptimizedOldLineShift;
}
}
@@ -31,7 +31,8 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.ForeignLeafPsiElement;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.text.ImmutableText;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.ImmutableCharSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
@@ -48,7 +49,7 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
private volatile Document mySyncDocument;
public PsiToDocumentSynchronizer(PsiDocumentManagerBase psiDocumentManager, MessageBus bus) {
PsiToDocumentSynchronizer(PsiDocumentManagerBase psiDocumentManager, MessageBus bus) {
myPsiDocumentManager = psiDocumentManager;
myBus = bus;
}
@@ -317,13 +318,11 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
}
});
private final PsiFile myChangeScope;
private final ImmutableText myDocText;
private ImmutableText myPsiText;
private ImmutableCharSequence myPsiText;
public DocumentChangeTransaction(@NotNull Document doc, @NotNull PsiFile scope) {
DocumentChangeTransaction(@NotNull Document doc, @NotNull PsiFile scope) {
myChangeScope = scope;
myDocText = ImmutableText.valueOf(doc.getImmutableCharSequence());
myPsiText = myDocText;
myPsiText = CharArrayUtil.createImmutableCharSequence(doc.getImmutableCharSequence());
}
@NotNull
@@ -571,4 +571,9 @@ public class CharArrayUtil {
//noinspection IOResourceOpenedButNotSafelyClosed
return chars == null ? new CharSequenceReader(text.toString()) : new UnsyncCharArrayReader(chars, 0, text.length());
}
@NotNull
public static ImmutableCharSequence createImmutableCharSequence(@NotNull CharSequence sequence) {
return ImmutableText.valueOf(sequence);
}
}
@@ -25,8 +25,14 @@ public abstract class ImmutableCharSequence implements CharSequence {
public static boolean isImmutable(@NotNull final CharSequence cs) {
if (cs instanceof ImmutableCharSequence) return true;
if (cs instanceof CharSequenceSubSequence) return isImmutable(((CharSequenceSubSequence)cs).getBaseSequence());
return false;
return cs instanceof CharSequenceSubSequence && isImmutable(((CharSequenceSubSequence)cs).getBaseSequence());
}
public abstract ImmutableCharSequence concat(CharSequence sequence);
public abstract ImmutableCharSequence insert(int index, CharSequence seq);
public abstract ImmutableCharSequence delete(int start, int end);
public abstract ImmutableCharSequence subtext(int start, int end);
}
@@ -55,7 +55,7 @@ import org.jetbrains.annotations.Nullable;
* @version 5.3, January 10, 2007
*/
@SuppressWarnings({"AssignmentToForLoopParameter","UnnecessaryThis"})
public final class ImmutableText extends ImmutableCharSequence implements CharArrayExternalizable, CharSequenceWithStringHash {
final class ImmutableText extends ImmutableCharSequence implements CharArrayExternalizable, CharSequenceWithStringHash {
/**
* Holds the default size for primitive blocks of characters.
*/
@@ -78,7 +78,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
* @param obj the object to represent as text.
* @return the textual representation of the specified object.
*/
public static ImmutableText valueOf(@NotNull Object obj) {
static ImmutableText valueOf(@NotNull Object obj) {
if (obj instanceof ImmutableText) return (ImmutableText)obj;
if (obj instanceof CharSequence) return ((CharSequence)obj).length() == 0 ? EMPTY : valueOf((CharSequence)obj);
return valueOf(String.valueOf(obj));
@@ -129,7 +129,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
*
* @return a copy of this text better prepared for small modifications to fully enable structure-sharing capabilities
*/
public ImmutableText ensureChunked() {
private ImmutableText ensureChunked() {
if (length() > BLOCK_SIZE && myNode instanceof LeafNode) {
return new ImmutableText(nodeOf((LeafNode)myNode, 0, length()));
}
@@ -145,22 +145,6 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
return new CompositeNode(nodeOf(node, offset, half), nodeOf(node, offset + half, length - half));
}
/**
* Returns the text representation of the <code>boolean</code> argument.
*
* @param b a <code>boolean</code>.
* @return if the argument is <code>true</code>, the text
* <code>"true"</code> is returned; otherwise, the text
* <code>"false"</code> is returned.
*/
public static ImmutableText valueOf(boolean b) {
return b ? TRUE : FALSE;
}
private static final ImmutableText TRUE = valueOf("true");
private static final ImmutableText FALSE = valueOf("false");
private static final LeafNode EMPTY_NODE = new Leaf8BitNode(ArrayUtil.EMPTY_BYTE_ARRAY);
private static final ImmutableText EMPTY = new ImmutableText(EMPTY_NODE);
@@ -183,10 +167,15 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
* @param that the text that is concatenated.
* @return <code>this + that</code>
*/
public ImmutableText concat(ImmutableText that) {
private ImmutableText concat(ImmutableText that) {
return that.length() == 0 ? this : length() == 0 ? that : new ImmutableText(concatNodes(ensureChunked().myNode, that.ensureChunked().myNode));
}
@Override
public ImmutableText concat(CharSequence sequence) {
return concat(valueOf(sequence));
}
/**
* Returns a portion of this text.
*
@@ -209,10 +198,11 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
* @throws IndexOutOfBoundsException if <code>(index < 0) ||
* (index > this.length())</code>
*/
public ImmutableText insert(int index, ImmutableText txt) {
private ImmutableText insert(int index, ImmutableText txt) {
return subtext(0, index).concat(txt).concat(subtext(index));
}
@Override
public ImmutableText insert(int index, CharSequence seq) {
return insert(index, valueOf(seq));
}
@@ -226,6 +216,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
* @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
* (start > end) || (end > this.length()</code>
*/
@Override
public ImmutableText delete(int start, int end) {
if (start == end) return this;
if (start > end) {
@@ -333,11 +324,12 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
* @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
* (start > end) || (end > this.length())</code>
*/
@Override
public ImmutableText subtext(int start, int end) {
if ((start < 0) || (start > end) || (end > length())) {
if (start < 0 || start > end || end > length()) {
throw new IndexOutOfBoundsException();
}
if ((start == 0) && (end == length())) {
if (start == 0 && end == length()) {
return this;
}
if (start == end) {
@@ -405,7 +397,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
Node head = node1;
Node tail = node2;
if (((head.length() << 1) < tail.length()) && tail instanceof CompositeNode) {
if ((head.length() << 1) < tail.length() && tail instanceof CompositeNode) {
// head too small, returns (head + tail/2) + (tail/2)
if (((CompositeNode)tail).head.length() > ((CompositeNode)tail).tail.length()) {
// Rotates to concatenate with smaller part.
@@ -414,7 +406,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
head = concatNodes(head, ((CompositeNode)tail).head);
tail = ((CompositeNode)tail).tail;
}
else if (((tail.length() << 1) < head.length()) && head instanceof CompositeNode) {
else if ((tail.length() << 1) < head.length() && head instanceof CompositeNode) {
// tail too small, returns (head/2) + (head/2 concat tail)
if (((CompositeNode)head).tail.length() > ((CompositeNode)head).head.length()) {
// Rotates to concatenate with smaller part.
@@ -441,7 +433,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
@Override
void getChars(int start, int end, @NotNull char[] dest, int destPos) {
if ((start < 0) || (end > length()) || (start > end)) {
if (start < 0 || end > length() || start > end) {
throw new IndexOutOfBoundsException();
}
System.arraycopy(data, start, dest, destPos, end - start);
@@ -480,7 +472,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
@Override
void getChars(int start, int end, @NotNull char[] dest, int destPos) {
if ((start < 0) || (end > length()) || (start > end)) {
if (start < 0 || end > length() || start > end) {
throw new IndexOutOfBoundsException();
}
for (int i=start;i<end;i++) {
@@ -579,7 +571,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr
if (start >= cesure) {
return tail.subNode(start - cesure, end - cesure);
}
if ((start == 0) && (end == count)) {
if (start == 0 && end == count) {
return this;
}
// Overlaps head and tail.