1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2016 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 /* 5 * ******************************************************************************** 6 * Copyright (C) 2007-2011, International Business Machines Corporation and others. 7 * All Rights Reserved. 8 * ******************************************************************************** 9 */ 10 package ohos.global.icu.impl; 11 12 import java.util.Iterator; 13 import java.util.LinkedList; 14 import java.util.List; 15 import java.util.ListIterator; 16 17 import ohos.global.icu.lang.UCharacter; 18 import ohos.global.icu.text.UnicodeSet; 19 20 /** 21 * TextTrieMap is a trie implementation for supporting 22 * fast prefix match for the key. 23 * @hide exposed on OHOS 24 */ 25 public class TextTrieMap<V> { 26 27 private Node _root = new Node(); 28 boolean _ignoreCase; 29 30 /** 31 * @hide exposed on OHOS 32 */ 33 public static class Output { 34 public int matchLength; 35 public boolean partialMatch; 36 } 37 38 /** 39 * Constructs a TextTrieMap object. 40 * 41 * @param ignoreCase true to use simple case insensitive match 42 */ TextTrieMap(boolean ignoreCase)43 public TextTrieMap(boolean ignoreCase) { 44 _ignoreCase = ignoreCase; 45 } 46 47 /** 48 * Adds the text key and its associated object in this object. 49 * 50 * @param text The text. 51 * @param val The value object associated with the text. 52 */ put(CharSequence text, V val)53 public TextTrieMap<V> put(CharSequence text, V val) { 54 CharIterator chitr = new CharIterator(text, 0, _ignoreCase); 55 _root.add(chitr, val); 56 return this; 57 } 58 59 /** 60 * Gets an iterator of the objects associated with the 61 * longest prefix matching string key. 62 * 63 * @param text The text to be matched with prefixes. 64 * @return An iterator of the objects associated with 65 * the longest prefix matching matching key, or null 66 * if no matching entry is found. 67 */ get(String text)68 public Iterator<V> get(String text) { 69 return get(text, 0); 70 } 71 72 /** 73 * Gets an iterator of the objects associated with the 74 * longest prefix matching string key starting at the 75 * specified position. 76 * 77 * @param text The text to be matched with prefixes. 78 * @param start The start index of of the text 79 * @return An iterator of the objects associated with the 80 * longest prefix matching matching key, or null if no 81 * matching entry is found. 82 */ get(CharSequence text, int start)83 public Iterator<V> get(CharSequence text, int start) { 84 return get(text, start, null); 85 } 86 get(CharSequence text, int start, Output output)87 public Iterator<V> get(CharSequence text, int start, Output output) { 88 LongestMatchHandler<V> handler = new LongestMatchHandler<V>(); 89 find(text, start, handler, output); 90 if (output != null) { 91 output.matchLength = handler.getMatchLength(); 92 } 93 return handler.getMatches(); 94 } 95 find(CharSequence text, ResultHandler<V> handler)96 public void find(CharSequence text, ResultHandler<V> handler) { 97 find(text, 0, handler, null); 98 } 99 find(CharSequence text, int offset, ResultHandler<V> handler)100 public void find(CharSequence text, int offset, ResultHandler<V> handler) { 101 find(text, offset, handler, null); 102 } 103 find(CharSequence text, int offset, ResultHandler<V> handler, Output output)104 private void find(CharSequence text, int offset, ResultHandler<V> handler, Output output) { 105 CharIterator chitr = new CharIterator(text, offset, _ignoreCase); 106 find(_root, chitr, handler, output); 107 } 108 find(Node node, CharIterator chitr, ResultHandler<V> handler, Output output)109 private synchronized void find(Node node, CharIterator chitr, ResultHandler<V> handler, Output output) { 110 Iterator<V> values = node.values(); 111 if (values != null) { 112 if (!handler.handlePrefixMatch(chitr.processedLength(), values)) { 113 return; 114 } 115 } 116 117 Node nextMatch = node.findMatch(chitr, output); 118 if (nextMatch != null) { 119 find(nextMatch, chitr, handler, output); 120 } 121 } 122 putLeadCodePoints(UnicodeSet output)123 public void putLeadCodePoints(UnicodeSet output) { 124 _root.putLeadCodePoints(output); 125 } 126 127 /** 128 * @hide exposed on OHOS 129 */ 130 public static class CharIterator implements Iterator<Character> { 131 private boolean _ignoreCase; 132 private CharSequence _text; 133 private int _nextIdx; 134 private int _startIdx; 135 136 private Character _remainingChar; 137 CharIterator(CharSequence text, int offset, boolean ignoreCase)138 CharIterator(CharSequence text, int offset, boolean ignoreCase) { 139 _text = text; 140 _nextIdx = _startIdx = offset; 141 _ignoreCase = ignoreCase; 142 } 143 144 /* (non-Javadoc) 145 * @see java.util.Iterator#hasNext() 146 */ 147 @Override hasNext()148 public boolean hasNext() { 149 if (_nextIdx == _text.length() && _remainingChar == null) { 150 return false; 151 } 152 return true; 153 } 154 155 /* (non-Javadoc) 156 * @see java.util.Iterator#next() 157 */ 158 @Override next()159 public Character next() { 160 if (_nextIdx == _text.length() && _remainingChar == null) { 161 return null; 162 } 163 Character next; 164 if (_remainingChar != null) { 165 next = _remainingChar; 166 _remainingChar = null; 167 } else { 168 if (_ignoreCase) { 169 int cp = UCharacter.foldCase(Character.codePointAt(_text, _nextIdx), true); 170 _nextIdx = _nextIdx + Character.charCount(cp); 171 172 char[] chars = Character.toChars(cp); 173 next = chars[0]; 174 if (chars.length == 2) { 175 _remainingChar = chars[1]; 176 } 177 } else { 178 next = _text.charAt(_nextIdx); 179 _nextIdx++; 180 } 181 } 182 return next; 183 } 184 185 /* (non-Javadoc) 186 * @see java.util.Iterator#remove() 187 */ 188 @Override remove()189 public void remove() { 190 throw new UnsupportedOperationException("remove() not supproted"); 191 } 192 nextIndex()193 public int nextIndex() { 194 return _nextIdx; 195 } 196 processedLength()197 public int processedLength() { 198 if (_remainingChar != null) { 199 throw new IllegalStateException("In the middle of surrogate pair"); 200 } 201 return _nextIdx - _startIdx; 202 } 203 } 204 205 /** 206 * Callback handler for processing prefix matches used by 207 * find method. 208 * @hide exposed on OHOS 209 */ 210 public interface ResultHandler<V> { 211 /** 212 * Handles a prefix key match 213 * 214 * @param matchLength Matched key's length 215 * @param values An iterator of the objects associated with the matched key 216 * @return Return true to continue the search in the trie, false to quit. 217 */ handlePrefixMatch(int matchLength, Iterator<V> values)218 public boolean handlePrefixMatch(int matchLength, Iterator<V> values); 219 } 220 221 private static class LongestMatchHandler<V> implements ResultHandler<V> { 222 private Iterator<V> matches = null; 223 private int length = 0; 224 225 @Override handlePrefixMatch(int matchLength, Iterator<V> values)226 public boolean handlePrefixMatch(int matchLength, Iterator<V> values) { 227 if (matchLength > length) { 228 length = matchLength; 229 matches = values; 230 } 231 return true; 232 } 233 getMatches()234 public Iterator<V> getMatches() { 235 return matches; 236 } 237 getMatchLength()238 public int getMatchLength() { 239 return length; 240 } 241 } 242 243 /** 244 * Inner class representing a text node in the trie. 245 */ 246 private class Node { 247 private char[] _text; 248 private List<V> _values; 249 private List<Node> _children; 250 Node()251 private Node() { 252 } 253 Node(char[] text, List<V> values, List<Node> children)254 private Node(char[] text, List<V> values, List<Node> children) { 255 _text = text; 256 _values = values; 257 _children = children; 258 } 259 charCount()260 public int charCount() { 261 return _text == null ? 0 : _text.length; 262 } 263 values()264 public Iterator<V> values() { 265 if (_values == null) { 266 return null; 267 } 268 return _values.iterator(); 269 } 270 add(CharIterator chitr, V value)271 public void add(CharIterator chitr, V value) { 272 StringBuilder buf = new StringBuilder(); 273 while (chitr.hasNext()) { 274 buf.append(chitr.next()); 275 } 276 add(toCharArray(buf), 0, value); 277 } 278 findMatch(CharIterator chitr, Output output)279 public Node findMatch(CharIterator chitr, Output output) { 280 if (_children == null) { 281 return null; 282 } 283 if (!chitr.hasNext()) { 284 if (output != null) { 285 output.partialMatch = true; 286 } 287 return null; 288 } 289 Node match = null; 290 Character ch = chitr.next(); 291 for (Node child : _children) { 292 if (ch < child._text[0]) { 293 break; 294 } 295 if (ch == child._text[0]) { 296 if (child.matchFollowing(chitr, output)) { 297 match = child; 298 } 299 break; 300 } 301 } 302 return match; 303 } 304 putLeadCodePoints(UnicodeSet output)305 public void putLeadCodePoints(UnicodeSet output) { 306 if (_children == null) { 307 return; 308 } 309 for (Node child : _children) { 310 char c0 = child._text[0]; 311 if (!UCharacter.isHighSurrogate(c0)) { 312 output.add(c0); 313 } else if (child.charCount() >= 2) { 314 output.add(Character.codePointAt(child._text, 0)); 315 } else if (child._children != null) { 316 // Construct all possible code points from grandchildren. 317 for (Node grandchild : child._children) { 318 char c1 = grandchild._text[0]; 319 int cp = Character.toCodePoint(c0, c1); 320 output.add(cp); 321 } 322 } 323 } 324 } 325 add(char[] text, int offset, V value)326 private void add(char[] text, int offset, V value) { 327 if (text.length == offset) { 328 _values = addValue(_values, value); 329 return; 330 } 331 332 if (_children == null) { 333 _children = new LinkedList<Node>(); 334 Node child = new Node(subArray(text, offset), addValue(null, value), null); 335 _children.add(child); 336 return; 337 } 338 339 // walk through children 340 ListIterator<Node> litr = _children.listIterator(); 341 while (litr.hasNext()) { 342 Node next = litr.next(); 343 if (text[offset] < next._text[0]) { 344 litr.previous(); 345 break; 346 } 347 if (text[offset] == next._text[0]) { 348 int matchLen = next.lenMatches(text, offset); 349 if (matchLen == next._text.length) { 350 // full match 351 next.add(text, offset + matchLen, value); 352 } else { 353 // partial match, create a branch 354 next.split(matchLen); 355 next.add(text, offset + matchLen, value); 356 } 357 return; 358 } 359 } 360 // add a new child to this node 361 litr.add(new Node(subArray(text, offset), addValue(null, value), null)); 362 } 363 matchFollowing(CharIterator chitr, Output output)364 private boolean matchFollowing(CharIterator chitr, Output output) { 365 boolean matched = true; 366 int idx = 1; 367 while (idx < _text.length) { 368 if(!chitr.hasNext()) { 369 if (output != null) { 370 output.partialMatch = true; 371 } 372 matched = false; 373 break; 374 } 375 Character ch = chitr.next(); 376 if (ch != _text[idx]) { 377 matched = false; 378 break; 379 } 380 idx++; 381 } 382 return matched; 383 } 384 lenMatches(char[] text, int offset)385 private int lenMatches(char[] text, int offset) { 386 int textLen = text.length - offset; 387 int limit = _text.length < textLen ? _text.length : textLen; 388 int len = 0; 389 while (len < limit) { 390 if (_text[len] != text[offset + len]) { 391 break; 392 } 393 len++; 394 } 395 return len; 396 } 397 398 private void split(int offset) { 399 // split the current node at the offset 400 char[] childText = subArray(_text, offset); 401 _text = subArray(_text, 0, offset); 402 403 // add the Node representing after the offset as a child 404 Node child = new Node(childText, _values, _children); 405 _values = null; 406 407 _children = new LinkedList<Node>(); 408 _children.add(child); 409 } 410 411 private List<V> addValue(List<V> list, V value) { 412 if (list == null) { 413 list = new LinkedList<V>(); 414 } 415 list.add(value); 416 return list; 417 } 418 } 419 420 private static char[] toCharArray(CharSequence text) { 421 char[] array = new char[text.length()]; 422 for (int i = 0; i < array.length; i++) { 423 array[i] = text.charAt(i); 424 } 425 return array; 426 } 427 428 private static char[] subArray(char[] array, int start) { 429 if (start == 0) { 430 return array; 431 } 432 char[] sub = new char[array.length - start]; 433 System.arraycopy(array, start, sub, 0, sub.length); 434 return sub; 435 } 436 437 private static char[] subArray(char[] array, int start, int limit) { 438 if (start == 0 && limit == array.length) { 439 return array; 440 } 441 char[] sub = new char[limit - start]; 442 System.arraycopy(array, start, sub, 0, limit - start); 443 return sub; 444 } 445 } 446