/*
* AbstractRope.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.ObjectStreamException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.ahmadsoft.ropes.Rope;
/**
* Abstract base class for ropes that implements many of the common operations.
* @author Amin Ahmad
*/
public abstract class AbstractRope implements Rope {
protected int hashCode = 0;
@Override
public Rope append(final char c) {
return RopeUtilities.INSTANCE.concatenate(this, Rope.BUILDER.build(String.valueOf(c)));
}
@Override
public Rope append(final CharSequence suffix) {
return RopeUtilities.INSTANCE.concatenate(this, Rope.BUILDER.build(suffix));
}
@Override
public Rope append(final CharSequence csq, final int start, final int end) {
return RopeUtilities.INSTANCE.concatenate(this, Rope.BUILDER.build(csq).subSequence(start, end));
}
@Override
public int compareTo(final CharSequence sequence) {
final int compareTill = Math.min(sequence.length(), this.length());
final Iterator i = this.iterator();
for (int j=0; j i1 = this.iterator();
final Iterator i2 = rope.iterator();
while (i1.hasNext()) {
final char a = i1.next();
final char b = i2.next();
if (a != b)
return false;
}
return true;
}
return false;
}
/**
* A utility method that returns an instance of this rope optimized
* for sequential access.
* @return
*/
protected CharSequence getForSequentialAccess() {
return this;
}
@Override
public int hashCode() {
if (this.hashCode == 0 && this.length() > 0) {
if (this.length() < 6) {
for (final char c: this)
this.hashCode = 31 * this.hashCode + c;
} else {
final Iterator i = this.iterator();
for (int j=0;j<5; ++j)
this.hashCode = 31 * this.hashCode + i.next();
this.hashCode = 31 * this.hashCode + this.charAt(this.length() - 1);
}
}
return this.hashCode;
}
@Override
public int indexOf(final char ch) {
int index = -1;
for (final char c: this) {
++index;
if (c == ch)
return index;
}
return -1;
}
@Override
public boolean startsWith(CharSequence prefix) {
return startsWith(prefix, 0);
}
@Override
public boolean startsWith(CharSequence prefix, int offset) {
if (offset < 0 || offset > this.length())
throw new IndexOutOfBoundsException("Rope offset out of range: " + offset);
if (offset + prefix.length() > this.length())
return false;
int x=0;
for (Iterator i=this.iterator(offset); i.hasNext() && x < prefix.length(); ) {
if (i.next().charValue() != prefix.charAt(x++))
return false;
}
return true;
}
@Override
public boolean endsWith(CharSequence suffix) {
return endsWith(suffix, 0);
}
@Override
public boolean endsWith(CharSequence suffix, int offset) {
return startsWith(suffix, length() - suffix.length() - offset);
}
@Override
public int indexOf(final char ch, final int fromIndex) {
if (fromIndex < 0 || fromIndex >= this.length())
throw new IndexOutOfBoundsException("Rope index out of range: " + fromIndex);
int index = fromIndex - 1;
for (final Iterator i=this.iterator(fromIndex); i.hasNext(); ) {
++index;
if (i.next().charValue() == ch)
return index;
}
return -1;
}
@Override
public int indexOf(final CharSequence sequence) {
return this.indexOf(sequence, 0);
}
@Override
public int indexOf(final CharSequence sequence, final int fromIndex) {
final CharSequence me = this.getForSequentialAccess();
// Implementation of Boyer-Moore-Horspool algorithm with
// special support for unicode.
// step 0. sanity check.
final int length = sequence.length();
if (length == 0)
return -1;
if (length == 1)
return this.indexOf(sequence.charAt(0), fromIndex);
final int[] bcs = new int[256]; // bad character shift
Arrays.fill(bcs, length);
// step 1. preprocessing.
for (int j=0; j this.length())
throw new IndexOutOfBoundsException(dstOffset + " is out of insert range [" + 0 + ":" + this.length() + "]");
return this.subSequence(0, dstOffset).append(r).append(this.subSequence(dstOffset, this.length()));
}
@Override
public Iterator iterator() {
return this.iterator(0);
}
@Override
public Rope trimStart() {
int index = -1;
for (final char c: this) {
++index;
if (c > 0x20 && !Character.isWhitespace(c))
break;
}
if (index <= 0)
return this;
else
return this.subSequence(index, this.length());
}
@Override
public Matcher matcher(final Pattern pattern) {
return pattern.matcher(this.getForSequentialAccess());
}
@Override
public boolean matches(final Pattern regex) {
return regex.matcher(this.getForSequentialAccess()).matches();
}
@Override
public boolean matches(final String regex) {
return Pattern.matches(regex, this.getForSequentialAccess());
}
@Override
public Rope rebalance() {
return this;
}
@Override
public Iterator reverseIterator() {
return this.reverseIterator(0);
}
@Override
public Rope trimEnd() {
int index = this.length() + 1;
for (final Iterator i=this.reverseIterator(); i.hasNext();) {
final char c = i.next();
--index;
if (c > 0x20 && !Character.isWhitespace(c))
break;
}
if (index >= this.length())
return this;
else
return this.subSequence(0, index);
}
@Override
public String toString() {
final StringWriter out = new StringWriter(this.length());
try {
this.write(out);
out.close();
} catch (final IOException e) {
throw new RuntimeException(e);
}
return out.toString();
}
@Override
public Rope trim() {
return this.trimStart().trimEnd();
}
public Object writeReplace() throws ObjectStreamException {
return new SerializedRope(this);
}
@Override
public Rope padStart(final int toWidth) {
return padStart(toWidth, ' ');
}
@Override
public Rope padStart(final int toWidth, final char padChar) {
final int toPad = toWidth - this.length();
if (toPad < 1)
return this;
return RopeUtilities.INSTANCE.concatenate(
Rope.BUILDER.build(new RepeatedCharacterSequence(padChar, toPad)),
this);
}
@Override
public Rope padEnd(final int toWidth) {
return padEnd(toWidth, ' ');
}
@Override
public Rope padEnd(final int toWidth, final char padChar) {
final int toPad = toWidth - this.length();
if (toPad < 1)
return this;
return RopeUtilities.INSTANCE.concatenate(
this,
Rope.BUILDER.build(new RepeatedCharacterSequence(padChar, toPad)));
}
@Override
public boolean isEmpty() {
return length() == 0;
}
}