/*
* ConcatenationRope.java
* Copyright (C) 2007 Amin Ahmad.
*
* This file is part of Java Ropes.
*
* Java Ropes is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Java Ropes is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Java Ropes. If not, see .
*
* Amin Ahmad can be contacted at amin.ahmad@gmail.com or on the web at
* www.ahmadsoft.org.
*/
package org.ahmadsoft.ropes.impl;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import org.ahmadsoft.ropes.Rope;
/**
* A rope that represents the concatenation of two other ropes.
* @author Amin Ahmad
*/
public final class ConcatenationRope extends AbstractRope {
private final Rope left;
private final Rope right;
private final byte depth;
private final int length;
/**
* Create a new concatenation rope from two ropes.
* @param left the first rope.
* @param right the second rope.
*/
public ConcatenationRope(final Rope left, final Rope right) {
this.left = left;
this.right = right;
this.depth = (byte) (Math.max(RopeUtilities.INSTANCE.depth(left), RopeUtilities.INSTANCE.depth(right)) + 1);
this.length = left.length() + right.length();
}
@Override
public char charAt(final int index) {
if (index >= this.length())
throw new IndexOutOfBoundsException("Rope index out of range: " + index);
return (index < this.left.length() ? this.left.charAt(index): this.right.charAt(index - this.left.length()));
}
@Override
public byte depth() {
return this.depth;
}
@Override
public CharSequence getForSequentialAccess() {
return this.getForSequentialAccess(this);
}
/*
* Returns this object as a char sequence optimized for
* regular expression searches.
*
* This method is public only to facilitate unit
* testing.
*/
private CharSequence getForSequentialAccess(final Rope rope) {
return new CharSequence() {
private final ConcatenationRopeIteratorImpl iterator = (ConcatenationRopeIteratorImpl) rope.iterator(0);
@Override
public char charAt(final int index) {
if (index > this.iterator.getPos()) {
this.iterator.skip(index-this.iterator.getPos()-1);
try {
final char c = this.iterator.next();
return c;
} catch (final IllegalArgumentException e) {
System.out.println("Rope length is: " + rope.length() + " charAt is " + index);
throw e;
}
} else { /* if (index <= lastIndex) */
final int toMoveBack = this.iterator.getPos() - index + 1;
if (this.iterator.canMoveBackwards(toMoveBack)) {
this.iterator.moveBackwards(toMoveBack);
return this.iterator.next();
} else {
return rope.charAt(index);
}
}
}
@Override
public int length() {
return rope.length();
}
@Override
public CharSequence subSequence(final int start, final int end) {
return rope.subSequence(start, end);
}
};
}
/**
* Return the left-hand rope.
* @return the left-hand rope.
*/
public Rope getLeft() {
return this.left;
}
/**
* Return the right-hand rope.
* @return the right-hand rope.
*/
public Rope getRight() {
return this.right;
}
@Override
public Iterator iterator(final int start) {
if (start < 0 || start > this.length())
throw new IndexOutOfBoundsException("Rope index out of range: " + start);
if (start >= this.left.length()) {
return this.right.iterator(start - this.left.length());
} else {
return new ConcatenationRopeIteratorImpl(this, start);
}
}
@Override
public int length() {
return this.length;
}
@Override
public Rope rebalance() {
return RopeUtilities.INSTANCE.rebalance(this);
}
@Override
public Rope reverse() {
return RopeUtilities.INSTANCE.concatenate(this.getRight().reverse(), this.getLeft().reverse());
}
@Override
public Iterator reverseIterator(final int start) {
if (start < 0 || start > this.length())
throw new IndexOutOfBoundsException("Rope index out of range: " + start);
if (start >= this.right.length()) {
return this.left.reverseIterator(start - this.right.length());
} else {
return new ConcatenationRopeReverseIteratorImpl(this, start);
}
}
@Override
public Rope subSequence(final int start, final int end) {
if (start < 0 || end > this.length())
throw new IllegalArgumentException("Illegal subsequence (" + start + "," + end + ")");
if (start == 0 && end == this.length())
return this;
final int l = this.left.length();
if (end <= l)
return this.left.subSequence(start, end);
if (start >= l)
return this.right.subSequence(start - l, end - l);
return RopeUtilities.INSTANCE.concatenate(
this.left.subSequence(start, l),
this.right.subSequence(0, end - l));
}
@Override
public void write(final Writer out) throws IOException {
this.left.write(out);
this.right.write(out);
}
@Override
public void write(final Writer out, final int offset, final int length) throws IOException {
if (offset + length <= this.left.length()) {
this.left.write(out, offset, length);
} else if (offset >= this.left.length()) {
this.right.write(out, offset - this.left.length(), length);
} else {
final int writeLeft = this.left.length() - offset;
this.left.write(out, offset, writeLeft);
this.right.write(out, 0, length - writeLeft);
}
}
// /**
// * Not currently used. Can be used if rebalancing is performed
// * during concatenation.
// **/
// private ConcatenationRope rotateLeft(final ConcatenationRope input) {
// final Rope _R = input.getRight();
// if (!(_R instanceof ConcatenationRope))
// return input;
// final ConcatenationRope R = (ConcatenationRope) _R;
// final Rope L = input.getLeft();
// final Rope A = R.getLeft();
// final Rope B = R.getRight();
// return new ConcatenationRope(new ConcatenationRope(L, A), B);
// }
//
// /**
// * Not currently used. Can be used if rebalancing is performed
// * during concatenation.
// **/
// private ConcatenationRope rotateRight(final ConcatenationRope input) {
// final Rope _L = input.getLeft();
// if (!(_L instanceof ConcatenationRope))
// return input;
// final ConcatenationRope L = (ConcatenationRope) _L;
// final Rope R = input.getRight();
// final Rope A = L.getLeft();
// final Rope B = L.getRight();
// return new ConcatenationRope(A, new ConcatenationRope(B, R));
// }
}