• 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) 2011-2014, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *******************************************************************************
9 *   created on: 2011jan05
10 *   created by: Markus W. Scherer
11 *   ported from ICU4C stringtriebuilder.h/.cpp
12 */
13 package ohos.global.icu.util;
14 
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 
18 /**
19  * Base class for string trie builder classes.
20  *
21  * <p>This class is not intended for public subclassing.
22  *
23  * @author Markus W. Scherer
24  * @hide exposed on OHOS
25  */
26 public abstract class StringTrieBuilder {
27     /**
28      * Build options for BytesTrieBuilder and CharsTrieBuilder.
29      * @hide exposed on OHOS
30      */
31     public enum Option {
32         /**
33          * Builds a trie quickly.
34          */
35         FAST,
36         /**
37          * Builds a trie more slowly, attempting to generate
38          * a shorter but equivalent serialization.
39          * This build option also uses more memory.
40          *
41          * <p>This option can be effective when many integer values are the same
42          * and string/byte sequence suffixes can be shared.
43          * Runtime speed is not expected to improve.
44          */
45         SMALL
46     }
47 
48     /**
49      * @deprecated This API is ICU internal only.
50      * @hide draft / provisional / internal are hidden on OHOS
51      */
52     @Deprecated
StringTrieBuilder()53     protected StringTrieBuilder() {}
54 
55     /**
56      * @deprecated This API is ICU internal only.
57      * @hide draft / provisional / internal are hidden on OHOS
58      */
59     @Deprecated
addImpl(CharSequence s, int value)60     protected void addImpl(CharSequence s, int value) {
61         if(state!=State.ADDING) {
62             // Cannot add elements after building.
63             throw new IllegalStateException("Cannot add (string, value) pairs after build().");
64         }
65         if(s.length()>0xffff) {
66             // Too long: Limited by iterator internals, and by builder recursion depth.
67             throw new IndexOutOfBoundsException("The maximum string length is 0xffff.");
68         }
69         if(root==null) {
70             root=createSuffixNode(s, 0, value);
71         } else {
72             root=root.add(this, s, 0, value);
73         }
74     }
75 
76     /**
77      * @deprecated This API is ICU internal only.
78      * @hide draft / provisional / internal are hidden on OHOS
79      */
80     @Deprecated
buildImpl(Option buildOption)81     protected final void buildImpl(Option buildOption) {
82         switch(state) {
83         case ADDING:
84             if(root==null) {
85                 throw new IndexOutOfBoundsException("No (string, value) pairs were added.");
86             }
87             if(buildOption==Option.FAST) {
88                 state=State.BUILDING_FAST;
89                 // Building "fast" is somewhat faster (25..50% in some test)
90                 // because it makes registerNode() return the input node
91                 // rather than checking for duplicates.
92                 // As a result, we sometimes write larger trie serializations.
93                 //
94                 // In either case we need to fix-up linear-match nodes (for their maximum length)
95                 // and branch nodes (turning dynamic branch nodes into trees of
96                 // runtime-equivalent nodes), but the HashMap/hashCode()/equals() are omitted for
97                 // nodes other than final values.
98             } else {
99                 state=State.BUILDING_SMALL;
100             }
101             break;
102         case BUILDING_FAST:
103         case BUILDING_SMALL:
104             // Building must have failed.
105             throw new IllegalStateException("Builder failed and must be clear()ed.");
106         case BUILT:
107             return;  // Nothing more to do.
108         }
109         // Implementation note:
110         // We really build three versions of the trie.
111         // The first is a fully dynamic trie, built successively by addImpl().
112         // Then we call root.register() to turn it into a tree of nodes
113         // which is 1:1 equivalent to the runtime data structure.
114         // Finally, root.markRightEdgesFirst() and root.write() write that serialized form.
115         root=root.register(this);
116         root.markRightEdgesFirst(-1);
117         root.write(this);
118         state=State.BUILT;
119     }
120 
121     /**
122      * @deprecated This API is ICU internal only.
123      * @hide draft / provisional / internal are hidden on OHOS
124      */
125     @Deprecated
clearImpl()126     protected void clearImpl() {
127         strings.setLength(0);
128         nodes.clear();
129         root=null;
130         state=State.ADDING;
131     }
132 
133     /**
134      * Makes sure that there is only one unique node registered that is
135      * equivalent to newNode, unless BUILDING_FAST.
136      * @param newNode Input node. The builder takes ownership.
137      * @return newNode if it is the first of its kind, or
138      *         an equivalent node if newNode is a duplicate.
139      */
registerNode(Node newNode)140     private final Node registerNode(Node newNode) {
141         if(state==State.BUILDING_FAST) {
142             return newNode;
143         }
144         // BUILDING_SMALL
145         Node oldNode=nodes.get(newNode);
146         if(oldNode!=null) {
147             return oldNode;
148         }
149         // If put() returns a non-null value from an equivalent, previously
150         // registered node, then get() failed to find that and we will leak newNode.
151         oldNode=nodes.put(newNode, newNode);
152         assert(oldNode==null);
153         return newNode;
154     }
155 
156     /**
157      * Makes sure that there is only one unique FinalValueNode registered
158      * with this value.
159      * Avoids creating a node if the value is a duplicate.
160      * @param value A final value.
161      * @return A FinalValueNode with the given value.
162      */
registerFinalValue(int value)163     private final ValueNode registerFinalValue(int value) {
164         // We always register final values because while ADDING
165         // we do not know yet whether we will build fast or small.
166         lookupFinalValueNode.setFinalValue(value);
167         Node oldNode=nodes.get(lookupFinalValueNode);
168         if(oldNode!=null) {
169             return (ValueNode)oldNode;
170         }
171         ValueNode newNode=new ValueNode(value);
172         // If put() returns a non-null value from an equivalent, previously
173         // registered node, then get() failed to find that and we will leak newNode.
174         oldNode=nodes.put(newNode, newNode);
175         assert(oldNode==null);
176         return newNode;
177     }
178 
179     private static abstract class Node {
Node()180         public Node() {
181             offset=0;
182         }
183         // hashCode() and equals() for use with registerNode() and the nodes hash.
184         @Override
hashCode()185         public abstract int hashCode() /*const*/;
186         // Base class equals() compares the actual class types.
187         @Override
equals(Object other)188         public boolean equals(Object other) {
189             return this==other || this.getClass()==other.getClass();
190         }
191         /**
192          * Recursive method for adding a new (string, value) pair.
193          * Matches the remaining part of s from start,
194          * and adds a new node where there is a mismatch.
195          * @return this or a replacement Node
196          */
add(StringTrieBuilder builder, CharSequence s, int start, int sValue)197         public Node add(StringTrieBuilder builder, CharSequence s, int start, int sValue) {
198             return this;
199         }
200         /**
201          * Recursive method for registering unique nodes,
202          * after all (string, value) pairs have been added.
203          * Final-value nodes are pre-registered while add()ing (string, value) pairs.
204          * Other nodes created while add()ing registerNode() themselves later
205          * and might replace themselves with new types of nodes for write()ing.
206          * @return The registered version of this node which implements write().
207          */
register(StringTrieBuilder builder)208         public Node register(StringTrieBuilder builder) { return this; }
209         /**
210          * Traverses the Node graph and numbers branch edges, with rightmost edges first.
211          * This is to avoid writing a duplicate node twice.
212          *
213          * Branch nodes in this trie data structure are not symmetric.
214          * Most branch edges "jump" to other nodes but the rightmost branch edges
215          * just continue without a jump.
216          * Therefore, write() must write the rightmost branch edge last
217          * (trie units are written backwards), and must write it at that point even if
218          * it is a duplicate of a node previously written elsewhere.
219          *
220          * This function visits and marks right branch edges first.
221          * Edges are numbered with increasingly negative values because we share the
222          * offset field which gets positive values when nodes are written.
223          * A branch edge also remembers the first number for any of its edges.
224          *
225          * When a further-left branch edge has a number in the range of the rightmost
226          * edge's numbers, then it will be written as part of the required right edge
227          * and we can avoid writing it first.
228          *
229          * After root.markRightEdgesFirst(-1) the offsets of all nodes are negative
230          * edge numbers.
231          *
232          * @param edgeNumber The first edge number for this node and its sub-nodes.
233          * @return An edge number that is at least the maximum-negative
234          *         of the input edge number and the numbers of this node and all of its sub-nodes.
235          */
markRightEdgesFirst(int edgeNumber)236         public int markRightEdgesFirst(int edgeNumber) {
237             if(offset==0) {
238                 offset=edgeNumber;
239             }
240             return edgeNumber;
241         }
242         // write() must set the offset to a positive value.
write(StringTrieBuilder builder)243         public abstract void write(StringTrieBuilder builder);
244         // See markRightEdgesFirst.
writeUnlessInsideRightEdge(int firstRight, int lastRight, StringTrieBuilder builder)245         public final void writeUnlessInsideRightEdge(int firstRight, int lastRight,
246                                                StringTrieBuilder builder) {
247             // Note: Edge numbers are negative, lastRight<=firstRight.
248             // If offset>0 then this node and its sub-nodes have been written already
249             // and we need not write them again.
250             // If this node is part of the unwritten right branch edge,
251             // then we wait until that is written.
252             if(offset<0 && (offset<lastRight || firstRight<offset)) {
253                 write(builder);
254             }
255         }
getOffset()256         public final int getOffset() /*const*/ { return offset; }
257 
258         protected int offset;
259     }
260 
261     // Used directly for final values, and as as a superclass for
262     // match nodes with intermediate values.
263     private static class ValueNode extends Node {
ValueNode()264         public ValueNode() {}
ValueNode(int v)265         public ValueNode(int v) {
266             hasValue=true;
267             value=v;
268         }
setValue(int v)269         public final void setValue(int v) {
270             assert(!hasValue);
271             hasValue=true;
272             value=v;
273         }
setFinalValue(int v)274         private void setFinalValue(int v) {
275             hasValue=true;
276             value=v;
277         }
278         @Override
hashCode()279         public int hashCode() /*const*/ {
280             int hash=0x111111;
281             if(hasValue) {
282                 hash=hash*37+value;
283             }
284             return hash;
285         }
286         @Override
equals(Object other)287         public boolean equals(Object other) {
288             if(this==other) {
289                 return true;
290             }
291             if(!super.equals(other)) {
292                 return false;
293             }
294             ValueNode o=(ValueNode)other;
295             return hasValue==o.hasValue && (!hasValue || value==o.value);
296         }
297         @Override
add(StringTrieBuilder builder, CharSequence s, int start, int sValue)298         public Node add(StringTrieBuilder builder, CharSequence s, int start, int sValue) {
299             if(start==s.length()) {
300                 throw new IllegalArgumentException("Duplicate string.");
301             }
302             // Replace self with a node for the remaining string suffix and value.
303             ValueNode node=builder.createSuffixNode(s, start, sValue);
304             node.setValue(value);
305             return node;
306         }
307         @Override
write(StringTrieBuilder builder)308         public void write(StringTrieBuilder builder) {
309             offset=builder.writeValueAndFinal(value, true);
310         }
311 
312         protected boolean hasValue;
313         protected int value;
314     }
315 
316     private static final class IntermediateValueNode extends ValueNode {
IntermediateValueNode(int v, Node nextNode)317         public IntermediateValueNode(int v, Node nextNode) {
318             next=nextNode;
319             setValue(v);
320         }
321         @Override
hashCode()322         public int hashCode() /*const*/ {
323             return (0x222222*37+value)*37+next.hashCode();
324         }
325         @Override
equals(Object other)326         public boolean equals(Object other) {
327             if(this==other) {
328                 return true;
329             }
330             if(!super.equals(other)) {
331                 return false;
332             }
333             IntermediateValueNode o=(IntermediateValueNode)other;
334             return next==o.next;
335         }
336         @Override
markRightEdgesFirst(int edgeNumber)337         public int markRightEdgesFirst(int edgeNumber) {
338             if(offset==0) {
339                 offset=edgeNumber=next.markRightEdgesFirst(edgeNumber);
340             }
341             return edgeNumber;
342         }
343         @Override
write(StringTrieBuilder builder)344         public void write(StringTrieBuilder builder) {
345             next.write(builder);
346             offset=builder.writeValueAndFinal(value, false);
347         }
348 
349         private Node next;
350     }
351 
352     private static final class LinearMatchNode extends ValueNode {
LinearMatchNode(CharSequence builderStrings, int sOffset, int len, Node nextNode)353         public LinearMatchNode(CharSequence builderStrings, int sOffset, int len, Node nextNode) {
354             strings=builderStrings;
355             stringOffset=sOffset;
356             length=len;
357             next=nextNode;
358         }
359         @Override
hashCode()360         public int hashCode() /*const*/ { return hash; }
361         @Override
equals(Object other)362         public boolean equals(Object other) {
363             if(this==other) {
364                 return true;
365             }
366             if(!super.equals(other)) {
367                 return false;
368             }
369             LinearMatchNode o=(LinearMatchNode)other;
370             if(length!=o.length || next!=o.next) {
371                 return false;
372             }
373             for(int i=stringOffset, j=o.stringOffset, limit=stringOffset+length; i<limit; ++i, ++j) {
374                 if(strings.charAt(i)!=strings.charAt(j)) {
375                     return false;
376                 }
377             }
378             return true;
379         }
380         @Override
add(StringTrieBuilder builder, CharSequence s, int start, int sValue)381         public Node add(StringTrieBuilder builder, CharSequence s, int start, int sValue) {
382             if(start==s.length()) {
383                 if(hasValue) {
384                     throw new IllegalArgumentException("Duplicate string.");
385                 } else {
386                     setValue(sValue);
387                     return this;
388                 }
389             }
390             int limit=stringOffset+length;
391             for(int i=stringOffset; i<limit; ++i, ++start) {
392                 if(start==s.length()) {
393                     // s is a prefix with a new value. Split self into two linear-match nodes.
394                     int prefixLength=i-stringOffset;
395                     LinearMatchNode suffixNode=new LinearMatchNode(strings, i, length-prefixLength, next);
396                     suffixNode.setValue(sValue);
397                     length=prefixLength;
398                     next=suffixNode;
399                     return this;
400                 }
401                 char thisChar=strings.charAt(i);
402                 char newChar=s.charAt(start);
403                 if(thisChar!=newChar) {
404                     // Mismatch, insert a branch node.
405                     DynamicBranchNode branchNode=new DynamicBranchNode();
406                     // Reuse this node for one of the remaining substrings, if any.
407                     Node result, thisSuffixNode;
408                     if(i==stringOffset) {
409                         // Mismatch on first character, turn this node into a suffix.
410                         if(hasValue) {
411                             // Move the value for prefix length "start" to the new node.
412                             branchNode.setValue(value);
413                             value=0;
414                             hasValue=false;
415                         }
416                         ++stringOffset;
417                         --length;
418                         thisSuffixNode= length>0 ? this : next;
419                         // C++: if(length==0) { delete this; }
420                         result=branchNode;
421                     } else if(i==limit-1) {
422                         // Mismatch on last character, keep this node for the prefix.
423                         --length;
424                         thisSuffixNode=next;
425                         next=branchNode;
426                         result=this;
427                     } else {
428                         // Mismatch on intermediate character, keep this node for the prefix.
429                         int prefixLength=i-stringOffset;
430                         ++i;  // Suffix start offset (after thisChar).
431                         thisSuffixNode=new LinearMatchNode(
432                                 strings, i, length-(prefixLength+1), next);
433                         length=prefixLength;
434                         next=branchNode;
435                         result=this;
436                     }
437                     ValueNode newSuffixNode=builder.createSuffixNode(s, start+1, sValue);
438                     branchNode.add(thisChar, thisSuffixNode);
439                     branchNode.add(newChar, newSuffixNode);
440                     return result;
441                 }
442             }
443             // s matches all of this node's characters.
444             next=next.add(builder, s, start, sValue);
445             return this;
446         }
447         @Override
register(StringTrieBuilder builder)448         public Node register(StringTrieBuilder builder) {
449             next=next.register(builder);
450             // Break the linear-match sequence into chunks of at most kMaxLinearMatchLength.
451             int maxLinearMatchLength=builder.getMaxLinearMatchLength();
452             while(length>maxLinearMatchLength) {
453                 int nextOffset=stringOffset+length-maxLinearMatchLength;
454                 length-=maxLinearMatchLength;
455                 LinearMatchNode suffixNode=
456                     new LinearMatchNode(strings, nextOffset, maxLinearMatchLength, next);
457                 suffixNode.setHashCode();
458                 next=builder.registerNode(suffixNode);
459             }
460             Node result;
461             if(hasValue && !builder.matchNodesCanHaveValues()) {
462                 int intermediateValue=value;
463                 value=0;
464                 hasValue=false;
465                 setHashCode();
466                 result=new IntermediateValueNode(intermediateValue, builder.registerNode(this));
467             } else {
468                 setHashCode();
469                 result=this;
470             }
471             return builder.registerNode(result);
472         }
473         @Override
markRightEdgesFirst(int edgeNumber)474         public int markRightEdgesFirst(int edgeNumber) {
475             if(offset==0) {
476                 offset=edgeNumber=next.markRightEdgesFirst(edgeNumber);
477             }
478             return edgeNumber;
479         }
480         @Override
write(StringTrieBuilder builder)481         public void write(StringTrieBuilder builder) {
482             next.write(builder);
483             builder.write(stringOffset, length);
484             offset=builder.writeValueAndType(hasValue, value, builder.getMinLinearMatch()+length-1);
485         }
486 
487         // Must be called just before registerNode(this).
setHashCode()488         private void setHashCode() /*const*/ {
489             hash=(0x333333*37+length)*37+next.hashCode();
490             if(hasValue) {
491                 hash=hash*37+value;
492             }
493             for(int i=stringOffset, limit=stringOffset+length; i<limit; ++i) {
494                 hash=hash*37+strings.charAt(i);
495             }
496         }
497 
498         private CharSequence strings;
499         private int stringOffset;
500         private int length;
501         private Node next;
502         private int hash;
503     }
504 
505     private static final class DynamicBranchNode extends ValueNode {
DynamicBranchNode()506         public DynamicBranchNode() {}
507         // c must not be in chars yet.
add(char c, Node node)508         public void add(char c, Node node) {
509             int i=find(c);
510             chars.insert(i, c);
511             equal.add(i, node);
512         }
513         @Override
add(StringTrieBuilder builder, CharSequence s, int start, int sValue)514         public Node add(StringTrieBuilder builder, CharSequence s, int start, int sValue) {
515             if(start==s.length()) {
516                 if(hasValue) {
517                     throw new IllegalArgumentException("Duplicate string.");
518                 } else {
519                     setValue(sValue);
520                     return this;
521                 }
522             }
523             char c=s.charAt(start++);
524             int i=find(c);
525             if(i<chars.length() && c==chars.charAt(i)) {
526                 equal.set(i, equal.get(i).add(builder, s, start, sValue));
527             } else {
528                 chars.insert(i, c);
529                 equal.add(i, builder.createSuffixNode(s, start, sValue));
530             }
531             return this;
532         }
533         @Override
register(StringTrieBuilder builder)534         public Node register(StringTrieBuilder builder) {
535             Node subNode=register(builder, 0, chars.length());
536             BranchHeadNode head=new BranchHeadNode(chars.length(), subNode);
537             Node result=head;
538             if(hasValue) {
539                 if(builder.matchNodesCanHaveValues()) {
540                     head.setValue(value);
541                 } else {
542                     result=new IntermediateValueNode(value, builder.registerNode(head));
543                 }
544             }
545             return builder.registerNode(result);
546         }
register(StringTrieBuilder builder, int start, int limit)547         private Node register(StringTrieBuilder builder, int start, int limit) {
548             int length=limit-start;
549             if(length>builder.getMaxBranchLinearSubNodeLength()) {
550                 // Branch on the middle unit.
551                 int middle=start+length/2;
552                 return builder.registerNode(
553                         new SplitBranchNode(
554                                 chars.charAt(middle),
555                                 register(builder, start, middle),
556                                 register(builder, middle, limit)));
557             }
558             ListBranchNode listNode=new ListBranchNode(length);
559             do {
560                 char c=chars.charAt(start);
561                 Node node=equal.get(start);
562                 if(node.getClass()==ValueNode.class) {
563                     // Final value.
564                     listNode.add(c, ((ValueNode)node).value);
565                 } else {
566                     listNode.add(c, node.register(builder));
567                 }
568             } while(++start<limit);
569             return builder.registerNode(listNode);
570         }
571 
find(char c)572         private int find(char c) {
573             int start=0;
574             int limit=chars.length();
575             while(start<limit) {
576                 int i=(start+limit)/2;
577                 char middleChar=chars.charAt(i);
578                 if(c<middleChar) {
579                     limit=i;
580                 } else if(c==middleChar) {
581                     return i;
582                 } else {
583                     start=i+1;
584                 }
585             }
586             return start;
587         }
588 
589         private StringBuilder chars=new StringBuilder();
590         private ArrayList<Node> equal=new ArrayList<Node>();
591     }
592 
593     private static abstract class BranchNode extends Node {
BranchNode()594         public BranchNode() {}
595         @Override
hashCode()596         public int hashCode() /*const*/ { return hash; }
597 
598         protected int hash;
599         protected int firstEdgeNumber;
600     }
601 
602     private static final class ListBranchNode extends BranchNode {
ListBranchNode(int capacity)603         public ListBranchNode(int capacity) {
604             hash=0x444444*37+capacity;
605             equal=new Node[capacity];
606             values=new int[capacity];
607             units=new char[capacity];
608         }
609         @Override
equals(Object other)610         public boolean equals(Object other) {
611             if(this==other) {
612                 return true;
613             }
614             if(!super.equals(other)) {
615                 return false;
616             }
617             ListBranchNode o=(ListBranchNode)other;
618             for(int i=0; i<length; ++i) {
619                 if(units[i]!=o.units[i] || values[i]!=o.values[i] || equal[i]!=o.equal[i]) {
620                     return false;
621                 }
622             }
623             return true;
624         }
625         @Override
hashCode()626         public int hashCode() {
627             return super.hashCode();
628         }
629         @Override
markRightEdgesFirst(int edgeNumber)630         public int markRightEdgesFirst(int edgeNumber) {
631             if(offset==0) {
632                 firstEdgeNumber=edgeNumber;
633                 int step=0;
634                 int i=length;
635                 do {
636                     Node edge=equal[--i];
637                     if(edge!=null) {
638                         edgeNumber=edge.markRightEdgesFirst(edgeNumber-step);
639                     }
640                     // For all but the rightmost edge, decrement the edge number.
641                     step=1;
642                 } while(i>0);
643                 offset=edgeNumber;
644             }
645             return edgeNumber;
646         }
647         @Override
write(StringTrieBuilder builder)648         public void write(StringTrieBuilder builder) {
649             // Write the sub-nodes in reverse order: The jump lengths are deltas from
650             // after their own positions, so if we wrote the minUnit sub-node first,
651             // then its jump delta would be larger.
652             // Instead we write the minUnit sub-node last, for a shorter delta.
653             int unitNumber=length-1;
654             Node rightEdge=equal[unitNumber];
655             int rightEdgeNumber= rightEdge==null ? firstEdgeNumber : rightEdge.getOffset();
656             do {
657                 --unitNumber;
658                 if(equal[unitNumber]!=null) {
659                     equal[unitNumber].writeUnlessInsideRightEdge(firstEdgeNumber, rightEdgeNumber, builder);
660                 }
661             } while(unitNumber>0);
662             // The maxUnit sub-node is written as the very last one because we do
663             // not jump for it at all.
664             unitNumber=length-1;
665             if(rightEdge==null) {
666                 builder.writeValueAndFinal(values[unitNumber], true);
667             } else {
668                 rightEdge.write(builder);
669             }
670             offset=builder.write(units[unitNumber]);
671             // Write the rest of this node's unit-value pairs.
672             while(--unitNumber>=0) {
673                 int value;
674                 boolean isFinal;
675                 if(equal[unitNumber]==null) {
676                     // Write the final value for the one string ending with this unit.
677                     value=values[unitNumber];
678                     isFinal=true;
679                 } else {
680                     // Write the delta to the start position of the sub-node.
681                     assert(equal[unitNumber].getOffset()>0);
682                     value=offset-equal[unitNumber].getOffset();
683                     isFinal=false;
684                 }
685                 builder.writeValueAndFinal(value, isFinal);
686                 offset=builder.write(units[unitNumber]);
687             }
688         }
689         // Adds a unit with a final value.
add(int c, int value)690         public void add(int c, int value) {
691             units[length]=(char)c;
692             equal[length]=null;
693             values[length]=value;
694             ++length;
695             hash=(hash*37+c)*37+value;
696         }
697         // Adds a unit which leads to another match node.
add(int c, Node node)698         public void add(int c, Node node) {
699             units[length]=(char)c;
700             equal[length]=node;
701             values[length]=0;
702             ++length;
703             hash=(hash*37+c)*37+node.hashCode();
704         }
705 
706         // Note: We could try to reduce memory allocations
707         // by replacing these per-node arrays with per-builder ArrayLists and
708         // (for units) a StringBuilder (or even use its strings for the units too).
709         // It remains to be seen whether that would improve performance.
710         private Node[] equal;  // null means "has final value".
711         private int length;
712         private int[] values;
713         private char[] units;
714     }
715 
716     private static final class SplitBranchNode extends BranchNode {
SplitBranchNode(char middleUnit, Node lessThanNode, Node greaterOrEqualNode)717         public SplitBranchNode(char middleUnit, Node lessThanNode, Node greaterOrEqualNode) {
718             hash=((0x555555*37+middleUnit)*37+
719                     lessThanNode.hashCode())*37+greaterOrEqualNode.hashCode();
720             unit=middleUnit;
721             lessThan=lessThanNode;
722             greaterOrEqual=greaterOrEqualNode;
723         }
724         @Override
equals(Object other)725         public boolean equals(Object other) {
726             if(this==other) {
727                 return true;
728             }
729             if(!super.equals(other)) {
730                 return false;
731             }
732             SplitBranchNode o=(SplitBranchNode)other;
733             return unit==o.unit && lessThan==o.lessThan && greaterOrEqual==o.greaterOrEqual;
734         }
735         @Override
hashCode()736         public int hashCode() {
737             return super.hashCode();
738         }
739         @Override
markRightEdgesFirst(int edgeNumber)740         public int markRightEdgesFirst(int edgeNumber) {
741             if(offset==0) {
742                 firstEdgeNumber=edgeNumber;
743                 edgeNumber=greaterOrEqual.markRightEdgesFirst(edgeNumber);
744                 offset=edgeNumber=lessThan.markRightEdgesFirst(edgeNumber-1);
745             }
746             return edgeNumber;
747         }
748         @Override
write(StringTrieBuilder builder)749         public void write(StringTrieBuilder builder) {
750             // Encode the less-than branch first.
751             lessThan.writeUnlessInsideRightEdge(firstEdgeNumber, greaterOrEqual.getOffset(), builder);
752             // Encode the greater-or-equal branch last because we do not jump for it at all.
753             greaterOrEqual.write(builder);
754             // Write this node.
755             assert(lessThan.getOffset()>0);
756             builder.writeDeltaTo(lessThan.getOffset());  // less-than
757             offset=builder.write(unit);
758         }
759 
760         private char unit;
761         private Node lessThan;
762         private Node greaterOrEqual;
763     }
764 
765     // Branch head node, for writing the actual node lead unit.
766     private static final class BranchHeadNode extends ValueNode {
BranchHeadNode(int len, Node subNode)767         public BranchHeadNode(int len, Node subNode) {
768             length=len;
769             next=subNode;
770         }
771         @Override
hashCode()772         public int hashCode() /*const*/ {
773             return (0x666666*37+length)*37+next.hashCode();
774         }
775         @Override
equals(Object other)776         public boolean equals(Object other) {
777             if(this==other) {
778                 return true;
779             }
780             if(!super.equals(other)) {
781                 return false;
782             }
783             BranchHeadNode o=(BranchHeadNode)other;
784             return length==o.length && next==o.next;
785         }
786         @Override
markRightEdgesFirst(int edgeNumber)787         public int markRightEdgesFirst(int edgeNumber) {
788             if(offset==0) {
789                 offset=edgeNumber=next.markRightEdgesFirst(edgeNumber);
790             }
791             return edgeNumber;
792         }
793         @Override
write(StringTrieBuilder builder)794         public void write(StringTrieBuilder builder) {
795             next.write(builder);
796             if(length<=builder.getMinLinearMatch()) {
797                 offset=builder.writeValueAndType(hasValue, value, length-1);
798             } else {
799                 builder.write(length-1);
800                 offset=builder.writeValueAndType(hasValue, value, 0);
801             }
802         }
803 
804         private int length;
805         private Node next;  // A branch sub-node.
806     }
807 
createSuffixNode(CharSequence s, int start, int sValue)808     private ValueNode createSuffixNode(CharSequence s, int start, int sValue) {
809         ValueNode node=registerFinalValue(sValue);
810         if(start<s.length()) {
811             int offset=strings.length();
812             strings.append(s, start, s.length());
813             node=new LinearMatchNode(strings, offset, s.length()-start, node);
814         }
815         return node;
816     }
817 
818     /**
819      * @deprecated This API is ICU internal only.
820      * @hide draft / provisional / internal are hidden on OHOS
821      */
822     @Deprecated
matchNodesCanHaveValues()823     protected abstract boolean matchNodesCanHaveValues() /*const*/;
824 
825     /**
826      * @deprecated This API is ICU internal only.
827      * @hide draft / provisional / internal are hidden on OHOS
828      */
829     @Deprecated
getMaxBranchLinearSubNodeLength()830     protected abstract int getMaxBranchLinearSubNodeLength() /*const*/;
831     /**
832      * @deprecated This API is ICU internal only.
833      * @hide draft / provisional / internal are hidden on OHOS
834      */
835     @Deprecated
getMinLinearMatch()836     protected abstract int getMinLinearMatch() /*const*/;
837     /**
838      * @deprecated This API is ICU internal only.
839      * @hide draft / provisional / internal are hidden on OHOS
840      */
841     @Deprecated
getMaxLinearMatchLength()842     protected abstract int getMaxLinearMatchLength() /*const*/;
843 
844     /**
845      * @deprecated This API is ICU internal only.
846      * @hide draft / provisional / internal are hidden on OHOS
847      */
848     @Deprecated
write(int unit)849     protected abstract int write(int unit);
850     /**
851      * @deprecated This API is ICU internal only.
852      * @hide draft / provisional / internal are hidden on OHOS
853      */
854     @Deprecated
write(int offset, int length)855     protected abstract int write(int offset, int length);
856     /**
857      * @deprecated This API is ICU internal only.
858      * @hide draft / provisional / internal are hidden on OHOS
859      */
860     @Deprecated
writeValueAndFinal(int i, boolean isFinal)861     protected abstract int writeValueAndFinal(int i, boolean isFinal);
862     /**
863      * @deprecated This API is ICU internal only.
864      * @hide draft / provisional / internal are hidden on OHOS
865      */
866     @Deprecated
writeValueAndType(boolean hasValue, int value, int node)867     protected abstract int writeValueAndType(boolean hasValue, int value, int node);
868     /**
869      * @deprecated This API is ICU internal only.
870      * @hide draft / provisional / internal are hidden on OHOS
871      */
872     @Deprecated
writeDeltaTo(int jumpTarget)873     protected abstract int writeDeltaTo(int jumpTarget);
874 
875     private enum State {
876         ADDING, BUILDING_FAST, BUILDING_SMALL, BUILT
877     }
878     private State state=State.ADDING;
879 
880     // Strings and sub-strings for linear-match nodes.
881     /**
882      * @deprecated This API is ICU internal only.
883      * @hide draft / provisional / internal are hidden on OHOS
884      */
885     @Deprecated
886     protected StringBuilder strings=new StringBuilder();
887     private Node root;
888 
889     // Hash set of nodes, maps from nodes to integer 1.
890     private HashMap<Node, Node> nodes=new HashMap<Node, Node>();
891     private ValueNode lookupFinalValueNode=new ValueNode();
892 }
893