1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 1996-2015, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.impl; 10 11 import com.ibm.icu.text.Replaceable; 12 import com.ibm.icu.text.ReplaceableString; 13 import com.ibm.icu.text.UCharacterIterator; 14 import com.ibm.icu.text.UTF16; 15 16 /** 17 * DLF docs must define behavior when Replaceable is mutated underneath 18 * the iterator. 19 * 20 * This and ICUCharacterIterator share some code, maybe they should share 21 * an implementation, or the common state and implementation should be 22 * moved up into UCharacterIterator. 23 * 24 * What are first, last, and getBeginIndex doing here?!?!?! 25 */ 26 public class ReplaceableUCharacterIterator extends UCharacterIterator { 27 28 // public constructor ------------------------------------------------------ 29 30 /** 31 * Public constructor 32 * @param replaceable text which the iterator will be based on 33 */ 34 public ReplaceableUCharacterIterator(Replaceable replaceable){ 35 if(replaceable==null){ 36 throw new IllegalArgumentException(); 37 } 38 this.replaceable = replaceable; 39 this.currentIndex = 0; 40 } 41 42 /** 43 * Public constructor 44 * @param str text which the iterator will be based on 45 */ 46 public ReplaceableUCharacterIterator(String str){ 47 if(str==null){ 48 throw new IllegalArgumentException(); 49 } 50 this.replaceable = new ReplaceableString(str); 51 this.currentIndex = 0; 52 } 53 54 /** 55 * Public constructor 56 * @param buf buffer of text on which the iterator will be based 57 */ 58 public ReplaceableUCharacterIterator(StringBuffer buf){ 59 if(buf==null){ 60 throw new IllegalArgumentException(); 61 } 62 this.replaceable = new ReplaceableString(buf); 63 this.currentIndex = 0; 64 } 65 66 // public methods ---------------------------------------------------------- 67 68 /** 69 * Creates a copy of this iterator, does not clone the underlying 70 * <code>Replaceable</code>object 71 * @return copy of this iterator 72 */ 73 @Override 74 public Object clone(){ 75 try { 76 return super.clone(); 77 } catch (CloneNotSupportedException e) { 78 return null; // never invoked 79 } 80 } 81 82 /** 83 * Returns the current UTF16 character. 84 * @return current UTF16 character 85 */ 86 @Override 87 public int current(){ 88 if (currentIndex < replaceable.length()) { 89 return replaceable.charAt(currentIndex); 90 } 91 return DONE; 92 } 93 94 /** 95 * Returns the current codepoint 96 * @return current codepoint 97 */ 98 @Override 99 public int currentCodePoint(){ 100 // cannot use charAt due to it different 101 // behaviour when index is pointing at a 102 // trail surrogate, check for surrogates 103 104 int ch = current(); 105 if(UTF16.isLeadSurrogate((char)ch)){ 106 // advance the index to get the next code point 107 next(); 108 // due to post increment semantics current() after next() 109 // actually returns the next char which is what we want 110 int ch2 = current(); 111 // current should never change the current index so back off 112 previous(); 113 114 if(UTF16.isTrailSurrogate((char)ch2)){ 115 // we found a surrogate pair 116 return Character.toCodePoint((char)ch, (char)ch2); 117 } 118 } 119 return ch; 120 } 121 122 /** 123 * Returns the length of the text 124 * @return length of the text 125 */ 126 @Override 127 public int getLength(){ 128 return replaceable.length(); 129 } 130 131 /** 132 * Gets the current currentIndex in text. 133 * @return current currentIndex in text. 134 */ 135 @Override 136 public int getIndex(){ 137 return currentIndex; 138 } 139 140 /** 141 * Returns next UTF16 character and increments the iterator's currentIndex by 1. 142 * If the resulting currentIndex is greater or equal to the text length, the 143 * currentIndex is reset to the text length and a value of DONECODEPOINT is 144 * returned. 145 * @return next UTF16 character in text or DONE if the new currentIndex is off the 146 * end of the text range. 147 */ 148 @Override 149 public int next(){ 150 if (currentIndex < replaceable.length()) { 151 return replaceable.charAt(currentIndex++); 152 } 153 return DONE; 154 } 155 156 157 /** 158 * Returns previous UTF16 character and decrements the iterator's currentIndex by 159 * 1. 160 * If the resulting currentIndex is less than 0, the currentIndex is reset to 0 and a 161 * value of DONECODEPOINT is returned. 162 * @return next UTF16 character in text or DONE if the new currentIndex is off the 163 * start of the text range. 164 */ 165 @Override 166 public int previous(){ 167 if (currentIndex > 0) { 168 return replaceable.charAt(--currentIndex); 169 } 170 return DONE; 171 } 172 173 /** 174 * <p>Sets the currentIndex to the specified currentIndex in the text and returns that 175 * single UTF16 character at currentIndex. 176 * This assumes the text is stored as 16-bit code units.</p> 177 * @param currentIndex the currentIndex within the text. 178 * @exception IllegalArgumentException is thrown if an invalid currentIndex is 179 * supplied. i.e. currentIndex is out of bounds. 180 * @returns the character at the specified currentIndex or DONE if the specified 181 * currentIndex is equal to the end of the text. 182 */ 183 @Override 184 public void setIndex(int currentIndex) throws IndexOutOfBoundsException{ 185 if (currentIndex < 0 || currentIndex > replaceable.length()) { 186 throw new IndexOutOfBoundsException(); 187 } 188 this.currentIndex = currentIndex; 189 } 190 191 @Override 192 public int getText(char[] fillIn, int offset){ 193 int length = replaceable.length(); 194 if(offset < 0 || offset + length > fillIn.length){ 195 throw new IndexOutOfBoundsException(Integer.toString(length)); 196 } 197 replaceable.getChars(0,length,fillIn,offset); 198 return length; 199 } 200 201 // private data members ---------------------------------------------------- 202 203 /** 204 * Replacable object 205 */ 206 private Replaceable replaceable; 207 /** 208 * Current currentIndex 209 */ 210 private int currentIndex; 211 212 } 213