1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2017 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 package ohos.global.icu.impl.number.parse; 5 6 import static ohos.global.icu.impl.number.parse.ParsingUtils.safeContains; 7 8 import ohos.global.icu.impl.StaticUnicodeSets; 9 import ohos.global.icu.impl.StringSegment; 10 import ohos.global.icu.impl.number.DecimalQuantity_DualStorageBCD; 11 import ohos.global.icu.impl.number.Grouper; 12 import ohos.global.icu.text.DecimalFormatSymbols; 13 import ohos.global.icu.text.UnicodeSet; 14 15 /** 16 * @author sffc 17 * @hide exposed on OHOS 18 * 19 */ 20 public class ScientificMatcher implements NumberParseMatcher { 21 22 private final String exponentSeparatorString; 23 private final DecimalMatcher exponentMatcher; 24 private final IgnorablesMatcher ignorablesMatcher; 25 private final String customMinusSign; 26 private final String customPlusSign; 27 getInstance(DecimalFormatSymbols symbols, Grouper grouper)28 public static ScientificMatcher getInstance(DecimalFormatSymbols symbols, Grouper grouper) { 29 // TODO: Static-initialize most common instances? 30 return new ScientificMatcher(symbols, grouper); 31 } 32 ScientificMatcher(DecimalFormatSymbols symbols, Grouper grouper)33 private ScientificMatcher(DecimalFormatSymbols symbols, Grouper grouper) { 34 exponentSeparatorString = symbols.getExponentSeparator(); 35 exponentMatcher = DecimalMatcher.getInstance(symbols, 36 grouper, 37 ParsingUtils.PARSE_FLAG_INTEGER_ONLY | ParsingUtils.PARSE_FLAG_GROUPING_DISABLED); 38 ignorablesMatcher = IgnorablesMatcher.getInstance(ParsingUtils.PARSE_FLAG_STRICT_IGNORABLES); 39 40 String minusSign = symbols.getMinusSignString(); 41 customMinusSign = safeContains(minusSignSet(), minusSign) ? null : minusSign; 42 String plusSign = symbols.getPlusSignString(); 43 customPlusSign = safeContains(plusSignSet(), plusSign) ? null : plusSign; 44 } 45 minusSignSet()46 private static UnicodeSet minusSignSet() { 47 return StaticUnicodeSets.get(StaticUnicodeSets.Key.MINUS_SIGN); 48 } 49 plusSignSet()50 private static UnicodeSet plusSignSet() { 51 return StaticUnicodeSets.get(StaticUnicodeSets.Key.PLUS_SIGN); 52 } 53 54 @Override match(StringSegment segment, ParsedNumber result)55 public boolean match(StringSegment segment, ParsedNumber result) { 56 // Only accept scientific notation after the mantissa. 57 if (!result.seenNumber()) { 58 return false; 59 } 60 61 // Only accept one exponent per string. 62 if (0 != (result.flags & ParsedNumber.FLAG_HAS_EXPONENT)) { 63 return false; 64 } 65 66 // First match the scientific separator, and then match another number after it. 67 // NOTE: This is guarded by the smoke test; no need to check exponentSeparatorString length again. 68 int initialOffset = segment.getOffset(); 69 int overlap = segment.getCommonPrefixLength(exponentSeparatorString); 70 if (overlap == exponentSeparatorString.length()) { 71 // Full exponent separator match. 72 73 // First attempt to get a code point, returning true if we can't get one. 74 if (segment.length() == overlap) { 75 return true; 76 } 77 segment.adjustOffset(overlap); 78 79 // Allow ignorables before the sign. 80 // Note: call site is guarded by the segment.length() check above. 81 ignorablesMatcher.match(segment, null); 82 if (segment.length() == 0) { 83 segment.setOffset(initialOffset); 84 return true; 85 } 86 87 // Allow a sign, and then try to match digits. 88 int exponentSign = 1; 89 if (segment.startsWith(minusSignSet())) { 90 exponentSign = -1; 91 segment.adjustOffsetByCodePoint(); 92 } else if (segment.startsWith(plusSignSet())) { 93 segment.adjustOffsetByCodePoint(); 94 } else if (segment.startsWith(customMinusSign)) { 95 overlap = segment.getCommonPrefixLength(customMinusSign); 96 if (overlap != customMinusSign.length()) { 97 // Partial custom sign match 98 segment.setOffset(initialOffset); 99 return true; 100 } 101 exponentSign = -1; 102 segment.adjustOffset(overlap); 103 } else if (segment.startsWith(customPlusSign)) { 104 overlap = segment.getCommonPrefixLength(customPlusSign); 105 if (overlap != customPlusSign.length()) { 106 // Partial custom sign match 107 segment.setOffset(initialOffset); 108 return true; 109 } 110 segment.adjustOffset(overlap); 111 } 112 113 // Return true if the segment is empty. 114 if (segment.length() == 0) { 115 segment.setOffset(initialOffset); 116 return true; 117 } 118 119 // Allow ignorables after the sign. 120 // Note: call site is guarded by the segment.length() check above. 121 ignorablesMatcher.match(segment, null); 122 if (segment.length() == 0) { 123 segment.setOffset(initialOffset); 124 return true; 125 } 126 127 // We are supposed to accept E0 after NaN, so we need to make sure result.quantity is available. 128 boolean wasNull = (result.quantity == null); 129 if (wasNull) { 130 result.quantity = new DecimalQuantity_DualStorageBCD(); 131 } 132 int digitsOffset = segment.getOffset(); 133 boolean digitsReturnValue = exponentMatcher.match(segment, result, exponentSign); 134 if (wasNull) { 135 result.quantity = null; 136 } 137 138 if (segment.getOffset() != digitsOffset) { 139 // At least one exponent digit was matched. 140 result.flags |= ParsedNumber.FLAG_HAS_EXPONENT; 141 } else { 142 // No exponent digits were matched 143 segment.setOffset(initialOffset); 144 } 145 return digitsReturnValue; 146 147 } else if (overlap == segment.length()) { 148 // Partial exponent separator match 149 return true; 150 } 151 152 // No match 153 return false; 154 } 155 156 @Override smokeTest(StringSegment segment)157 public boolean smokeTest(StringSegment segment) { 158 return segment.startsWith(exponentSeparatorString); 159 } 160 161 @Override postProcess(ParsedNumber result)162 public void postProcess(ParsedNumber result) { 163 // No-op 164 } 165 166 @Override toString()167 public String toString() { 168 return "<ScientificMatcher " + exponentSeparatorString + ">"; 169 } 170 } 171