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