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) 1996-2015, Google, Inc., International Business Machines Corporation and 7 * others. All Rights Reserved. 8 ******************************************************************************* 9 */ 10 package ohos.global.icu.impl; 11 12 import java.util.Collection; 13 import java.util.Comparator; 14 import java.util.Iterator; 15 import java.util.LinkedList; 16 import java.util.Map.Entry; 17 import java.util.Set; 18 import java.util.TreeMap; 19 import java.util.TreeSet; 20 21 import ohos.global.icu.lang.CharSequences; 22 import ohos.global.icu.util.ICUException; 23 24 /** 25 * @hide exposed on OHOS 26 */ 27 @SuppressWarnings("deprecation") 28 public class StringRange { 29 private static final boolean DEBUG = false; 30 31 /** 32 * @hide exposed on OHOS 33 */ 34 public interface Adder { 35 /** 36 * @param start 37 * @param end may be null, for adding single string 38 */ add(String start, String end)39 void add(String start, String end); 40 } 41 42 public static final Comparator<int[]> COMPARE_INT_ARRAYS = new Comparator<int[]>() { 43 @Override 44 public int compare(int[] o1, int[] o2) { 45 int minIndex = Math.min(o1.length, o2.length); 46 for (int i = 0; i < minIndex; ++i) { 47 int diff = o1[i] - o2[i]; 48 if (diff != 0) { 49 return diff; 50 } 51 } 52 return o1.length - o2.length; 53 } 54 }; 55 56 /** 57 * Compact the set of strings. 58 * @param source set of strings 59 * @param adder adds each pair to the output. See the {@link Adder} interface. 60 * @param shorterPairs use abc-d instead of abc-abd 61 * @param moreCompact use a more compact form, at the expense of more processing. If false, source must be sorted. 62 */ compact(Set<String> source, Adder adder, boolean shorterPairs, boolean moreCompact)63 public static void compact(Set<String> source, Adder adder, boolean shorterPairs, boolean moreCompact) { 64 if (!moreCompact) { 65 String start = null; 66 String end = null; 67 int lastCp = 0; 68 int prefixLen = 0; 69 for (String s : source) { 70 if (start != null) { // We have something queued up 71 if (s.regionMatches(0, start, 0, prefixLen)) { 72 int currentCp = s.codePointAt(prefixLen); 73 if (currentCp == 1+lastCp && s.length() == prefixLen + Character.charCount(currentCp)) { 74 end = s; 75 lastCp = currentCp; 76 continue; 77 } 78 } 79 // We failed to find continuation. Add what we have and restart 80 adder.add(start, end == null ? null 81 : !shorterPairs ? end 82 : end.substring(prefixLen, end.length())); 83 } 84 // new possible range 85 start = s; 86 end = null; 87 lastCp = s.codePointBefore(s.length()); 88 prefixLen = s.length() - Character.charCount(lastCp); 89 } 90 adder.add(start, end == null ? null 91 : !shorterPairs ? end 92 : end.substring(prefixLen, end.length())); 93 } else { 94 // not a fast algorithm, but ok for now 95 // TODO rewire to use the first (slower) algorithm to generate the ranges, then compact them from there. 96 // first sort by lengths 97 Relation<Integer,Ranges> lengthToArrays = Relation.of(new TreeMap<Integer,Set<Ranges>>(), TreeSet.class); 98 for (String s : source) { 99 Ranges item = new Ranges(s); 100 lengthToArrays.put(item.size(), item); 101 } 102 // then compact items of each length and emit compacted sets 103 for (Entry<Integer, Set<Ranges>> entry : lengthToArrays.keyValuesSet()) { 104 LinkedList<Ranges> compacted = compact(entry.getKey(), entry.getValue()); 105 for (Ranges ranges : compacted) { 106 adder.add(ranges.start(), ranges.end(shorterPairs)); 107 } 108 } 109 } 110 } 111 112 /** 113 * Faster but not as good compaction. Only looks at final codepoint. 114 * @param source set of strings 115 * @param adder adds each pair to the output. See the {@link Adder} interface. 116 * @param shorterPairs use abc-d instead of abc-abd 117 */ compact(Set<String> source, Adder adder, boolean shorterPairs)118 public static void compact(Set<String> source, Adder adder, boolean shorterPairs) { 119 compact(source,adder,shorterPairs,false); 120 } 121 compact(int size, Set<Ranges> inputRanges)122 private static LinkedList<Ranges> compact(int size, Set<Ranges> inputRanges) { 123 LinkedList<Ranges> ranges = new LinkedList<Ranges>(inputRanges); 124 for (int i = size-1; i >= 0; --i) { 125 Ranges last = null; 126 for (Iterator<Ranges> it = ranges.iterator(); it.hasNext();) { 127 Ranges item = it.next(); 128 if (last == null) { 129 last = item; 130 } else if (last.merge(i, item)) { 131 it.remove(); 132 } else { 133 last = item; // go to next 134 } 135 } 136 }; 137 return ranges; 138 } 139 140 static final class Range implements Comparable<Range>{ 141 int min; 142 int max; Range(int min, int max)143 public Range(int min, int max) { 144 this.min = min; 145 this.max = max; 146 } 147 @Override equals(Object obj)148 public boolean equals(Object obj) { 149 return this == obj || (obj != null && obj instanceof Range && compareTo((Range)obj) == 0); 150 } 151 @Override compareTo(Range that)152 public int compareTo(Range that) { 153 int diff = min - that.min; 154 if (diff != 0) { 155 return diff; 156 } 157 return max - that.max; 158 } 159 @Override hashCode()160 public int hashCode() { 161 return min * 37 + max; 162 } 163 @Override toString()164 public String toString() { 165 StringBuilder result = new StringBuilder().appendCodePoint(min); 166 return min == max ? result.toString() : result.append('~').appendCodePoint(max).toString(); 167 } 168 } 169 170 static final class Ranges implements Comparable<Ranges> { 171 private final Range[] ranges; Ranges(String s)172 public Ranges(String s) { 173 int[] array = CharSequences.codePoints(s); 174 ranges = new Range[array.length]; 175 for (int i = 0; i < array.length; ++i) { 176 ranges[i] = new Range(array[i], array[i]); 177 } 178 } merge(int pivot, Ranges other)179 public boolean merge(int pivot, Ranges other) { 180 // We merge items if the pivot is adjacent, and all later ranges are equal. 181 for (int i = ranges.length-1; i >= 0; --i) { 182 if (i == pivot) { 183 if (ranges[i].max != other.ranges[i].min-1) { // not adjacent 184 return false; 185 } 186 } else { 187 if (!ranges[i].equals(other.ranges[i])) { 188 return false; 189 } 190 } 191 } 192 if (DEBUG) System.out.print("Merging: " + this + ", " + other); 193 ranges[pivot].max = other.ranges[pivot].max; 194 if (DEBUG) System.out.println(" => " + this); 195 return true; 196 } 197 start()198 public String start() { 199 StringBuilder result = new StringBuilder(); 200 for (int i = 0; i < ranges.length; ++i) { 201 result.appendCodePoint(ranges[i].min); 202 } 203 return result.toString(); 204 } end(boolean mostCompact)205 public String end(boolean mostCompact) { 206 int firstDiff = firstDifference(); 207 if (firstDiff == ranges.length) { 208 return null; 209 } 210 StringBuilder result = new StringBuilder(); 211 for (int i = mostCompact ? firstDiff : 0; i < ranges.length; ++i) { 212 result.appendCodePoint(ranges[i].max); 213 } 214 return result.toString(); 215 } firstDifference()216 public int firstDifference() { 217 for (int i = 0; i < ranges.length; ++i) { 218 if (ranges[i].min != ranges[i].max){ 219 return i; 220 } 221 } 222 return ranges.length; 223 } size()224 public Integer size() { 225 return ranges.length; 226 } 227 @Override compareTo(Ranges other)228 public int compareTo(Ranges other) { 229 int diff = ranges.length - other.ranges.length; 230 if (diff != 0) { 231 return diff; 232 } 233 for (int i = 0; i < ranges.length; ++i) { 234 diff = ranges[i].compareTo(other.ranges[i]); 235 if (diff != 0) { 236 return diff; 237 } 238 } 239 return 0; 240 } 241 @Override toString()242 public String toString() { 243 String start = start(); 244 String end = end(false); 245 return end == null ? start : start + "~" + end; 246 } 247 } 248 expand(String start, String end, boolean requireSameLength, Collection<String> output)249 public static Collection<String> expand(String start, String end, boolean requireSameLength, Collection<String> output) { 250 if (start == null || end == null) { 251 throw new ICUException("Range must have 2 valid strings"); 252 } 253 int[] startCps = CharSequences.codePoints(start); 254 int[] endCps = CharSequences.codePoints(end); 255 int startOffset = startCps.length - endCps.length; 256 257 if (requireSameLength && startOffset != 0) { 258 throw new ICUException("Range must have equal-length strings"); 259 } else if (startOffset < 0) { 260 throw new ICUException("Range must have start-length ≥ end-length"); 261 } else if (endCps.length == 0) { 262 throw new ICUException("Range must have end-length > 0"); 263 } 264 265 StringBuilder builder = new StringBuilder(); 266 for (int i = 0; i < startOffset; ++i) { 267 builder.appendCodePoint(startCps[i]); 268 } 269 add(0, startOffset, startCps, endCps, builder, output); 270 return output; 271 } 272 add(int endIndex, int startOffset, int[] starts, int[] ends, StringBuilder builder, Collection<String> output)273 private static void add(int endIndex, int startOffset, int[] starts, int[] ends, StringBuilder builder, Collection<String> output) { 274 int start = starts[endIndex+startOffset]; 275 int end = ends[endIndex]; 276 if (start > end) { 277 throw new ICUException("Range must have xᵢ ≤ yᵢ for each index i"); 278 } 279 boolean last = endIndex == ends.length - 1; 280 int startLen = builder.length(); 281 for (int i = start; i <= end; ++i) { 282 builder.appendCodePoint(i); 283 if (last) { 284 output.add(builder.toString()); 285 } else { 286 add(endIndex+1, startOffset, starts, ends, builder, output); 287 } 288 builder.setLength(startLen); 289 } 290 } 291 } 292