blob: 857ca2fc331a6afd2a505e247d088caf2171a7b3 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2011, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.text;
import org.eclipse.jface.text.BadLocationException;
public abstract class CharCodepointIterator implements ICodepointIterator {
private static final byte PREPARE_STEPBACK= (PREPARE_BACKWARD | PREPARE_FIX);
private static class StringIterator extends CharCodepointIterator {
private final String fString;
private final int fStringOffset;
public StringIterator(final String string, final int stringIndex,
final int beginIndex, final int endIndex) throws BadLocationException {
super(beginIndex, endIndex);
this.fString= string;
this.fStringOffset= stringIndex;
}
@Override
protected char getChar(final int offset, final byte prepare) {
return this.fString.charAt(offset - this.fStringOffset);
}
@Override
public String toString() {
return this.fString.substring(getBeginIndex() - this.fStringOffset, getEndIndex() - this.fStringOffset);
}
}
private static class CharArrayIterator extends CharCodepointIterator {
private final char[] fArray;
private final int fArrayOffset;
public CharArrayIterator(final char[] array, final int arrayIndex,
final int beginIndex, final int endIndex) throws BadLocationException {
super(beginIndex, endIndex);
this.fArray= array;
this.fArrayOffset= arrayIndex;
}
@Override
protected char getChar(final int offset, final byte prepare) {
return this.fArray[offset - this.fArrayOffset];
}
@Override
public String toString() {
return new String(this.fArray, getBeginIndex() - this.fArrayOffset, getEndIndex() - getBeginIndex());
}
}
/**
* Creates a new iterator for a string.
*
* @param string the string
* @param stringIndex the offset of the string in the document
* @param beginIndex the begin index of the iterator in the document
* @param endIndex the end index of the iterator in the document
* @throws BadLocationException if an index is not valid
*/
public static CharCodepointIterator create(final String string, final int stringIndex,
final int beginIndex, final int endIndex)
throws BadLocationException {
if (beginIndex > endIndex
|| beginIndex < stringIndex || endIndex > stringIndex + string.length()) {
throw new BadLocationException();
}
return new StringIterator(string, stringIndex, beginIndex, endIndex);
}
/**
* Creates a new iterator for a char array.
*
* @param array the char array
* @param arrayIndex the offset of the array in the document
* @param beginIndex the begin index of the iterator in the document
* @param endIndex the end index of the iterator in the document
* @throws BadLocationException if an index is not valid
*/
public static CharCodepointIterator create(final char[] array, final int arrayIndex,
final int beginIndex, final int endIndex)
throws BadLocationException {
if (beginIndex > endIndex
|| beginIndex < arrayIndex || endIndex > arrayIndex + array.length) {
throw new BadLocationException();
}
return new CharArrayIterator(array, arrayIndex, beginIndex, endIndex);
}
private final int fBeginIndex;
private final int fEndIndex;
private int fCurrentIndex;
private int fCurrentCodepoint;
private int fCurrentCharLength;
protected CharCodepointIterator(final int beginIndex, final int endIndex) {
this.fBeginIndex= beginIndex;
this.fEndIndex= endIndex;
}
protected abstract char getChar(int index, byte prepare);
@Override
public final int first() {
internalSet(this.fBeginIndex, PREPARE_FORWARD);
return this.fCurrentCodepoint;
}
@Override
public final int last() {
internalSet((this.fBeginIndex < this.fEndIndex) ? this.fEndIndex - 1 : this.fEndIndex, PREPARE_STEPBACK);
return this.fCurrentCodepoint;
}
@Override
public final int current() {
return this.fCurrentCodepoint;
}
@Override
public final int next() {
if (this.fCurrentIndex < this.fEndIndex) {
internalSet(this.fCurrentIndex + this.fCurrentCharLength, PREPARE_FORWARD);
return this.fCurrentCodepoint;
}
else {
return EOF;
}
}
public final int next(int count) {
while (count > 0 && this.fCurrentIndex < this.fEndIndex) {
internalSet(this.fCurrentIndex + this.fCurrentCharLength, PREPARE_FORWARD);
count--;
}
return (count == 0) ? this.fCurrentCodepoint : EOF;
}
@Override
public final int previous() {
if (this.fCurrentIndex > this.fBeginIndex) {
internalSet(this.fCurrentIndex - 1, PREPARE_STEPBACK);
return this.fCurrentCodepoint;
}
else {
return EOF;
}
}
public final int previous(int count) {
while (count > 0 && this.fCurrentIndex > this.fBeginIndex) {
internalSet(this.fCurrentIndex - 1, PREPARE_STEPBACK);
count--;
}
return (count == 0) ? this.fCurrentCodepoint : EOF;
}
@Override
public void setIndex(final int index, final byte prepare) throws BadLocationException {
if (index < this.fBeginIndex || index > this.fEndIndex) {
throw new BadLocationException();
}
internalSet(index, prepare);
}
private final void internalSet(final int index, final byte prepare) {
this.fCurrentIndex= index;
if (this.fCurrentIndex < this.fEndIndex) {
final char c= getChar(this.fCurrentIndex, prepare);
char c2;
if ((prepare & PREPARE_FIX) != 0 && Character.isLowSurrogate(c)
&& this.fCurrentIndex > this.fBeginIndex
&& Character.isHighSurrogate(c2= getChar(this.fCurrentIndex-1, prepare)) ) {
this.fCurrentIndex--;
this.fCurrentCodepoint= Character.toCodePoint(c2, c);
this.fCurrentCharLength= 2;
}
else if (Character.isHighSurrogate(c)
&& (this.fCurrentIndex+1 < this.fEndIndex
&& Character.isLowSurrogate((c2= getChar(this.fCurrentIndex+1, prepare)) ))) {
this.fCurrentCodepoint= Character.toCodePoint(c, c2);
this.fCurrentCharLength= 2;
}
else {
this.fCurrentCodepoint= c;
this.fCurrentCharLength= 1;
}
}
else {
this.fCurrentCodepoint= EOF;
this.fCurrentCharLength= 0;
}
}
@Override
public final int getBeginIndex() {
return this.fBeginIndex;
}
@Override
public final int getEndIndex() {
return this.fEndIndex;
}
@Override
public final int getCurrentIndex() {
return this.fCurrentIndex;
}
@Override
public final int getCurrentLength() {
return this.fCurrentCharLength;
}
}