| /*=============================================================================# |
| # 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; |
| } |
| |
| } |