diff --git a/platform/util/src/com/intellij/util/text/ImmutableText.java b/platform/util/src/com/intellij/util/text/ImmutableText.java index 2dde3eb60293..10ef583400cd 100644 --- a/platform/util/src/com/intellij/util/text/ImmutableText.java +++ b/platform/util/src/com/intellij/util/text/ImmutableText.java @@ -50,9 +50,8 @@ import org.jetbrains.annotations.NotNull; * @author Wilfried Middleton * @version 5.3, January 10, 2007 */ -@SuppressWarnings("AssignmentToForLoopParameter") +@SuppressWarnings({"AssignmentToForLoopParameter","UnnecessaryThis"}) public final class ImmutableText extends ImmutableCharSequence implements CharArrayExternalizable { - /** * Holds the default size for primitive blocks of characters. */ @@ -82,18 +81,22 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr } private static ImmutableText valueOf(@NotNull CharSequence str) { - return new ImmutableText(new LeafNode(CharArrayUtil.fromSequence(str, 0, str.length()))); + return new ImmutableText(createLeafNode(CharArrayUtil.fromSequence(str, 0, str.length()))); } /** - * Returns the text that contains the characters from the specified + * Returns the text that contains the characters from the specified * array. * * @param chars the array source of the characters. * @return the corresponding instance. */ public static ImmutableText valueOf(@NotNull char[] chars) { - return new ImmutableText(new LeafNode(chars)); + return new ImmutableText(createLeafNode(chars)); + } + + private static LeafNode createLeafNode(@NotNull char[] chars) { + return new LeafNode(chars); } /** @@ -105,26 +108,20 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr */ public ImmutableText ensureChunked() { if (length() > BLOCK_SIZE && myNode instanceof LeafNode) { - return new ImmutableText(nodeOf(((LeafNode)myNode)._data, 0, length())); + return new ImmutableText(nodeOf((LeafNode)myNode, 0, length())); } return this; } - private static Node nodeOf(@NotNull char[] chars, int offset, int length) { + private static Node nodeOf(@NotNull LeafNode node, int offset, int length) { if (length <= BLOCK_SIZE) { - if (offset == 0 && length == chars.length) { - return new LeafNode(chars); - } - char[] subArray = new char[length]; - System.arraycopy(chars, offset, subArray, 0, length); - return new LeafNode(subArray); - } else { // Splits on a block boundary. - int half = ((length + BLOCK_SIZE) >> 1) & BLOCK_MASK; - return new CompositeNode(nodeOf(chars, offset, half), nodeOf(chars, offset + half, length - half)); + return node.subNode(offset, offset+length); } + // Splits on a block boundary. + int half = ((length + BLOCK_SIZE) >> 1) & BLOCK_MASK; + return new CompositeNode(nodeOf(node, offset, half), nodeOf(node, offset + half, length - half)); } - /** * Returns the text representation of the boolean argument. * @@ -141,13 +138,15 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr private static final ImmutableText FALSE = valueOf("false"); - private static final ImmutableText EMPTY = valueOf(""); + private static final LeafNode EMPTY_NODE = new LeafNode(new char[0]); + private static final ImmutableText EMPTY = new ImmutableText(EMPTY_NODE); /** * Returns the length of this text. * * @return the number of characters (16-bits Unicode) composing this text. */ + @Override public int length() { return myNode.nodeLength(); } @@ -162,7 +161,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @return this + that */ public ImmutableText concat(ImmutableText that) { - return that.length() == 0 ? this : new ImmutableText(ensureChunked().myNode.concatNodes(that.ensureChunked().myNode)); + return that.length() == 0 ? this : new ImmutableText(concatNodes(ensureChunked().myNode, that.ensureChunked().myNode)); } /** @@ -206,28 +205,35 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr */ public ImmutableText delete(int start, int end) { if (start == end) return this; - if (start > end) + if (start > end) { throw new IndexOutOfBoundsException(); + } return ensureChunked().subtext(0, start).concat(subtext(end)); } + @Override public CharSequence subSequence(final int start, final int end) { if (start == 0 && end == length()) return this; return new CharSequenceSubSequence(this, start, end); } + @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (!(obj instanceof ImmutableText)) + } + if (!(obj instanceof ImmutableText)) { return false; - final ImmutableText that = (ImmutableText) obj; + } + final ImmutableText that = (ImmutableText)obj; int len = this.length(); - if (len != that.length()) + if (len != that.length()) { return false; - for (int i = 0; i < len;) { - if (this.charAt(i) != that.charAt(i++)) + } + for (int i = 0; i < len; ) { + if (this.charAt(i) != that.charAt(i++)) { return false; + } } return true; } @@ -237,6 +243,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * * @return the hash code value. */ + @Override public int hashCode() { int h = 0; final int length = this.length(); @@ -246,16 +253,17 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr return h; } + @Override public char charAt(int index) { if (myNode instanceof LeafNode) { - return ((LeafNode)myNode)._data[index]; + return ((LeafNode)myNode).charAt(index); } InnerLeaf leaf = myLastLeaf; if (leaf == null || index < leaf.offset || index >= leaf.offset + leaf.leafNode.nodeLength()) { myLastLeaf = leaf = findLeaf(index, 0); } - return leaf.leafNode._data[index - leaf.offset]; + return leaf.leafNode.charAt(index - leaf.offset); } private volatile InnerLeaf myLastLeaf; @@ -271,7 +279,8 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr CompositeNode composite = (CompositeNode)node; if (index < composite._head.nodeLength()) { node = composite._head; - } else { + } + else { offset += composite._head.nodeLength(); index -= composite._head.nodeLength(); node = composite._tail; @@ -283,7 +292,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr final LeafNode leafNode; final int offset; - private InnerLeaf(LeafNode leafNode, int offset) { + private InnerLeaf(@NotNull LeafNode leafNode, int offset) { this.leafNode = leafNode; this.offset = offset; } @@ -300,12 +309,15 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * (start > end) || (end > this.length()) */ 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) + } + if (start == end) { return EMPTY; + } return new ImmutableText(myNode.subNode(start, end)); } @@ -321,6 +333,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @throws IndexOutOfBoundsException if (start < 0) || (end < 0) || * (start > end) || (end > this.length()) */ + @Override public void getChars(int start, int end, @NotNull char[] dest, int destPos) { myNode.getChars(start, end, dest, destPos); } @@ -330,68 +343,66 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * * @return the java.lang.String for this text. */ + @Override @NotNull public String toString() { - if (myNode instanceof LeafNode) { // Primitive. - return new String(((LeafNode)myNode)._data, 0, length()); - } else { // Composite. - int len = length(); + return myNode.toString(); + } + + private abstract static class Node { + abstract int nodeLength(); + abstract void getChars(int start, int end, @NotNull char[] dest, int destPos); + abstract Node subNode(int start, int end); + @Override + public String toString() { + int len = nodeLength(); char[] data = new char[len]; - this.getChars(0, len, data, 0); - return new String(data, 0, len); + getChars(0, len, data, 0); + return StringFactory.createShared(data); } } - private static abstract class Node { - - abstract int nodeLength(); - - Node concatNodes(Node that) { - // All Text instances are maintained balanced: - // (head < tail * 2) & (tail < head * 2) - - final int length = this.nodeLength() + that.nodeLength(); - if (length <= BLOCK_SIZE) { // Merges to primitive. - char[] chars = new char[length]; - this.getChars(0, this.nodeLength(), chars, 0); - that.getChars(0, that.nodeLength(), chars, this.nodeLength()); - return new LeafNode(chars); - } else { // Returns a composite. - Node head = this; - Node tail = that; - - if (((head.nodeLength() << 1) < tail.nodeLength()) && tail instanceof CompositeNode) { - // head too small, returns (head + tail/2) + (tail/2) - if (((CompositeNode)tail)._head.nodeLength() > ((CompositeNode)tail)._tail.nodeLength()) { - // Rotates to concatenate with smaller part. - tail = ((CompositeNode)tail).rightRotation(); - } - head = head.concatNodes(((CompositeNode)tail)._head); - tail = ((CompositeNode)tail)._tail; - - } else if (((tail.nodeLength() << 1) < head.nodeLength()) && head instanceof CompositeNode) { - // tail too small, returns (head/2) + (head/2 concat tail) - if (((CompositeNode)head)._tail.nodeLength() > ((CompositeNode)head)._head.nodeLength()) { - // Rotates to concatenate with smaller part. - head = ((CompositeNode)head).leftRotation(); - } - tail = ((CompositeNode)head)._tail.concatNodes(tail); - head = ((CompositeNode)head)._head; - } - return new CompositeNode(head, tail); - } + @NotNull + private static Node concatNodes(@NotNull Node node1, @NotNull Node node2) { + // All Text instances are maintained balanced: + // (head < tail * 2) & (tail < head * 2) + final int length = node1.nodeLength() + node2.nodeLength(); + if (length <= BLOCK_SIZE) { // Merges to primitive. + char[] chars = new char[length]; + node1.getChars(0, node1.nodeLength(), chars, 0); + node2.getChars(0, node2.nodeLength(), chars, node1.nodeLength()); + return createLeafNode(chars); } + else { // Returns a composite. + Node head = node1; + Node tail = node2; - abstract void getChars(int start, int end, @NotNull char[] dest, int destPos); - - abstract Node subNode(int start, int end); - + if (((head.nodeLength() << 1) < tail.nodeLength()) && tail instanceof CompositeNode) { + // head too small, returns (head + tail/2) + (tail/2) + if (((CompositeNode)tail)._head.nodeLength() > ((CompositeNode)tail)._tail.nodeLength()) { + // Rotates to concatenate with smaller part. + tail = ((CompositeNode)tail).rightRotation(); + } + head = concatNodes(head, ((CompositeNode)tail)._head); + tail = ((CompositeNode)tail)._tail; + } + else if (((tail.nodeLength() << 1) < head.nodeLength()) && head instanceof CompositeNode) { + // tail too small, returns (head/2) + (head/2 concat tail) + if (((CompositeNode)head)._tail.nodeLength() > ((CompositeNode)head)._head.nodeLength()) { + // Rotates to concatenate with smaller part. + head = ((CompositeNode)head).leftRotation(); + } + tail = concatNodes(((CompositeNode)head)._tail, tail); + head = ((CompositeNode)head)._head; + } + return new CompositeNode(head, tail); + } } private static class LeafNode extends Node { - final char[] _data; + private final char[] _data; - LeafNode(char[] _data) { + LeafNode(@NotNull char[] _data) { this._data = _data; } @@ -402,8 +413,9 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr @Override void getChars(int start, int end, @NotNull char[] dest, int destPos) { - if ((start < 0) || (end > nodeLength()) || (start > end)) + if ((start < 0) || (end > nodeLength()) || (start > end)) { throw new IndexOutOfBoundsException(); + } System.arraycopy(_data, start, dest, destPos, end - start); } @@ -415,7 +427,16 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr int length = end - start; char[] chars = new char[length]; System.arraycopy(_data, start, chars, 0, length); - return new LeafNode(chars); + return createLeafNode(chars); + } + + @Override + public String toString() { + return StringFactory.createShared(_data); + } + + public char charAt(int index) { + return _data[index]; } } @@ -438,8 +459,9 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr Node rightRotation() { // See: http://en.wikipedia.org/wiki/Tree_rotation Node P = this._head; - if (!(P instanceof CompositeNode)) + if (!(P instanceof CompositeNode)) { return this; // Head not a composite, cannot rotate. + } Node A = ((CompositeNode)P)._head; Node B = ((CompositeNode)P)._tail; Node C = this._tail; @@ -449,8 +471,9 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr Node leftRotation() { // See: http://en.wikipedia.org/wiki/Tree_rotation Node Q = this._tail; - if (!(Q instanceof CompositeNode)) + if (!(Q instanceof CompositeNode)) { return this; // Tail not a composite, cannot rotate. + } Node B = ((CompositeNode)Q)._head; Node C = ((CompositeNode)Q)._tail; Node A = this._head; @@ -462,9 +485,11 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr final int cesure = _head.nodeLength(); if (end <= cesure) { _head.getChars(start, end, dest, destPos); - } else if (start >= cesure) { + } + else if (start >= cesure) { _tail.getChars(start - cesure, end - cesure, dest, destPos); - } else { // Overlaps head and tail. + } + else { // Overlaps head and tail. _head.getChars(start, cesure, dest, destPos); _tail.getChars(0, end - cesure, dest, destPos + cesure - start); } @@ -473,14 +498,17 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr @Override Node subNode(int start, int end) { final int cesure = _head.nodeLength(); - if (end <= cesure) + if (end <= cesure) { return _head.subNode(start, end); - if (start >= cesure) + } + 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. - return _head.subNode(start, cesure).concatNodes(_tail.subNode(0, end - cesure)); + return concatNodes(_head.subNode(start, cesure), _tail.subNode(0, end - cesure)); } } } \ No newline at end of file