/*=============================================================================# # Copyright (c) 2011-2016 Stephan Wahlbrink (WalWare.de) and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # # Contributors: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.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); fString = string; fStringOffset = stringIndex; } @Override protected char getChar(final int offset, final byte prepare) { return fString.charAt(offset - fStringOffset); } @Override public String toString() { return fString.substring(getBeginIndex() - fStringOffset, getEndIndex() - 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); fArray = array; fArrayOffset = arrayIndex; } @Override protected char getChar(final int offset, final byte prepare) { return fArray[offset - fArrayOffset]; } @Override public String toString() { return new String(fArray, getBeginIndex() - 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) { fBeginIndex = beginIndex; fEndIndex = endIndex; } protected abstract char getChar(int index, byte prepare); @Override public final int first() { internalSet(fBeginIndex, PREPARE_FORWARD); return fCurrentCodepoint; } @Override public final int last() { internalSet((fBeginIndex < fEndIndex) ? fEndIndex - 1 : fEndIndex, PREPARE_STEPBACK); return fCurrentCodepoint; } @Override public final int current() { return fCurrentCodepoint; } @Override public final int next() { if (fCurrentIndex < fEndIndex) { internalSet(fCurrentIndex + fCurrentCharLength, PREPARE_FORWARD); return fCurrentCodepoint; } else { return EOF; } } public final int next(int count) { while (count > 0 && fCurrentIndex < fEndIndex) { internalSet(fCurrentIndex + fCurrentCharLength, PREPARE_FORWARD); count--; } return (count == 0) ? fCurrentCodepoint : EOF; } @Override public final int previous() { if (fCurrentIndex > fBeginIndex) { internalSet(fCurrentIndex - 1, PREPARE_STEPBACK); return fCurrentCodepoint; } else { return EOF; } } public final int previous(int count) { while (count > 0 && fCurrentIndex > fBeginIndex) { internalSet(fCurrentIndex - 1, PREPARE_STEPBACK); count--; } return (count == 0) ? fCurrentCodepoint : EOF; } @Override public void setIndex(final int index, final byte prepare) throws BadLocationException { if (index < fBeginIndex || index > fEndIndex) { throw new BadLocationException(); } internalSet(index, prepare); } private final void internalSet(final int index, final byte prepare) { fCurrentIndex = index; if (fCurrentIndex < fEndIndex) { final char c = getChar(fCurrentIndex, prepare); char c2; if ((prepare & PREPARE_FIX) != 0 && Character.isLowSurrogate(c) && fCurrentIndex > fBeginIndex && Character.isHighSurrogate(c2 = getChar(fCurrentIndex-1, prepare)) ) { fCurrentIndex--; fCurrentCodepoint = Character.toCodePoint(c2, c); fCurrentCharLength = 2; } else if (Character.isHighSurrogate(c) && (fCurrentIndex+1 < fEndIndex && Character.isLowSurrogate((c2 = getChar(fCurrentIndex+1, prepare)) ))) { fCurrentCodepoint = Character.toCodePoint(c, c2); fCurrentCharLength = 2; } else { fCurrentCodepoint = c; fCurrentCharLength = 1; } } else { fCurrentCodepoint = EOF; fCurrentCharLength = 0; } } @Override public final int getBeginIndex() { return fBeginIndex; } @Override public final int getEndIndex() { return fEndIndex; } @Override public final int getCurrentIndex() { return fCurrentIndex; } @Override public final int getCurrentLength() { return fCurrentCharLength; } }