/** * Prints out this text to the specified writer. * * @param writer the destination writer. */ public void print(Writer writer) throws IOException { if (_data != null) { // Primitive writer.write(_data, 0, _count); } else { // Composite. _head.print(writer); _tail.print(writer); } }
/** * Compares this text against the specified object for equality. Returns <code>true</code> if the * specified object is a text having the same character sequence as this text. For generic * comparison with any character sequence the {@link #contentEquals(CharSequence)} should be used. * * @param obj the object to compare with or <code>null</code>. * @return <code>true</code> if that is a text with the same character sequence as this text; * <code>false</code> otherwise. */ public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof ImmutableText)) return false; final ImmutableText that = (ImmutableText) obj; if (this._count != that._count) return false; for (int i = 0; i < _count; ) { if (this.charAt(i) != that.charAt(i++)) return false; } return true; }
/** * Returns the index within this text of the first occurrence of the specified character, * searching backward and starting at the specified index. * * @param c the character to search for. * @param fromIndex the index to start the search backward from. * @return the index of the first occurrence of the character in this text that is less than or * equal to <code>fromIndex</code>, or <code>-1</code> if the character does not occur. */ public int lastIndexOf(char c, int fromIndex) { if (_data != null) { // Primitive. for (int i = Math.min(fromIndex, _count - 1); i >= 0; i--) { if (_data[i] == c) return i; } return -1; } else { // Composite. final int cesure = _head._count; if (fromIndex >= cesure) { final int tailIndex = _tail.lastIndexOf(c, fromIndex - cesure); if (tailIndex >= 0) return tailIndex + cesure; // Found in tail. } return _head.lastIndexOf(c, fromIndex); } }
/** * Copies the characters from this text into the destination character array. * * @param start the index of the first character to copy. * @param end the index after the last character to copy. * @param dest the destination array. * @param destPos the start offset in the destination array. * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) || * (start > end) || (end > this.length())</code> */ public void getChars(int start, int end, @NotNull char[] dest, int destPos) { if (_data != null) { // Primitive. if ((start < 0) || (end > _count) || (start > end)) throw new IndexOutOfBoundsException(); System.arraycopy(_data, start, dest, destPos, end - start); } else { // Composite. final int cesure = _head._count; if (end <= cesure) { _head.getChars(start, end, dest, destPos); } else if (start >= cesure) { _tail.getChars(start - cesure, end - cesure, dest, destPos); } else { // Overlaps head and tail. _head.getChars(start, cesure, dest, destPos); _tail.getChars(0, end - cesure, dest, destPos + cesure - start); } } }
/** * Returns the index within this text of the first occurrence of the specified character, starting * the search at the specified index. * * @param c the character to search for. * @param fromIndex the index to start the search from. * @return the index of the first occurrence of the character in this text that is greater than or * equal to <code>fromIndex</code>, or <code>-1</code> if the character does not occur. */ public int indexOf(char c, int fromIndex) { if (_data != null) { // Primitive. for (int i = Math.max(fromIndex, 0); i < _count; i++) { if (_data[i] == c) return i; } return -1; } else { // Composite. final int cesure = _head._count; if (fromIndex < cesure) { final int headIndex = _head.indexOf(c, fromIndex); if (headIndex >= 0) return headIndex; // Found in head. } final int tailIndex = _tail.indexOf(c, fromIndex - cesure); return (tailIndex >= 0) ? tailIndex + cesure : -1; } }
/** * Returns a portion of this text. * * @param start the index of the first character inclusive. * @param end the index of the last character exclusive. * @return the sub-text starting at the specified start position and ending just before the * specified end position. * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) || * (start > end) || (end > this.length())</code> */ public ImmutableText subtext(int start, int end) { if (_data != null) { // Primitive. if ((start < 0) || (start > end) || (end > _count)) throw new IndexOutOfBoundsException(); if ((start == 0) && (end == _count)) return this; if (start == end) return EMPTY; int length = end - start; char[] chars = new char[length]; System.arraycopy(_data, start, chars, 0, length); return new ImmutableText(chars); } else { // Composite. final int cesure = _head._count; if (end <= cesure) return _head.subtext(start, end); if (start >= cesure) return _tail.subtext(start - cesure, end - cesure); if ((start == 0) && (end == _count)) return this; // Overlaps head and tail. return _head.subtext(start, cesure).concat(_tail.subtext(0, end - cesure)); } }
private ImmutableText append(String str) { // Try to append, returns null if cannot. int length = str.length(); if (_data == null) { ImmutableText merge = _tail.append(str); return merge != null ? new ImmutableText(_head, merge) : null; } else { // Primitive. if (_count + length > BLOCK_SIZE) return null; // Cannot merge. char[] chars = new char[_count + length]; System.arraycopy(_data, 0, chars, 0, _count); str.getChars(0, length, chars, _count); return new ImmutableText(chars); } }
/** * Concatenates the specified text to the end of this text. This method is very fast (faster even * than <code>StringBuffer.append(String)</code>) and still returns a text instance with an * internal binary tree of minimal depth! * * @param that the text that is concatenated. * @return <code>this + that</code> */ public ImmutableText concat(ImmutableText that) { if (that.length() == 0) { return this; } // All Text instances are maintained balanced: // (head < tail * 2) & (tail < head * 2) final int length = this._count + that._count; if (length <= BLOCK_SIZE) { // Merges to primitive. char[] chars = new char[length]; this.getChars(0, this._count, chars, 0); that.getChars(0, that._count, chars, this._count); return new ImmutableText(chars); } else { // Returns a composite. ImmutableText head = this; ImmutableText tail = that; if (((head._count << 1) < tail._count) && (tail._data == null)) { // tail is composite // head too small, returns (head + tail/2) + (tail/2) if (tail._head._count > tail._tail._count) { // Rotates to concatenate with smaller part. tail = tail.rightRotation(); } head = head.concat(tail._head); tail = tail._tail; } else if (((tail._count << 1) < head._count) && (head._data == null)) { // head is composite. // tail too small, returns (head/2) + (head/2 concat tail) if (head._tail._count > head._head._count) { // Rotates to concatenate with smaller part. head = head.leftRotation(); } tail = head._tail.concat(tail); head = head._head; } return new ImmutableText(head, tail); } }
private int getNbrOfLeaves() { return (_data == null) ? _head.getNbrOfLeaves() + _tail.getNbrOfLeaves() : 1; }
private int getNbrOfBranches() { return (_data == null) ? _head.getNbrOfBranches() + _tail.getNbrOfBranches() + 1 : 0; }
private int getDepth() { if (_data != null) // Primitive. return 0; return Math.max(_head.getDepth(), _tail.getDepth()) + 1; }