• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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